Merge branch '4599_bash_prompt_array'
[midnight-commander.git] / src / consaver / cons.saver.c
blob5b04fdd28ce26dfb3054e24d4b2192ae1f06187d
1 /*
2 General purpose Linux console screen save/restore server
4 This program should be setuid vcsa and /dev/vcsa* should be
5 owned by the same user too.
7 Original idea from Unix Interactive Tools version 3.2b (tty.c)
8 This code requires root privileges.
9 You may want to make the cons.saver setuid root.
10 The code should be safe even if it is setuid but who knows?
12 Partly rewritten by Jakub Jelinek <jakub@redhat.com>.
14 Copyright (C) 1994-2024
15 Free Software Foundation, Inc.
17 This file is part of the Midnight Commander.
19 The Midnight Commander is free software: you can redistribute it
20 and/or modify it under the terms of the GNU General Public License as
21 published by the Free Software Foundation, either version 3 of the License,
22 or (at your option) any later version.
24 The Midnight Commander is distributed in the hope that it will be useful,
25 but WITHOUT ANY WARRANTY; without even the implied warranty of
26 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 GNU General Public License for more details.
29 You should have received a copy of the GNU General Public License
30 along with this program. If not, see <http://www.gnu.org/licenses/>.
33 /* This code does _not_ need to be setuid root. However, it needs
34 read/write access to /dev/vcsa* (which is privileged
35 operation). You should create user vcsa, make cons.saver setuid
36 user vcsa, and make all vcsa's owned by user vcsa.
38 Seeing other peoples consoles is bad thing, but believe me, full
39 root is even worse. */
41 /** \file cons.saver.c
42 * \brief Source: general purpose Linux console screen save/restore server
44 * This code does _not_ need to be setuid root. However, it needs
45 * read/write access to /dev/vcsa* (which is privileged
46 * operation). You should create user vcsa, make cons.saver setuid
47 * user vcsa, and make all vcsa's owned by user vcsa.
48 * Seeing other peoples consoles is bad thing, but believe me, full
49 * root is even worse.
52 #include <config.h>
54 #ifndef _GNU_SOURCE
55 #define _GNU_SOURCE
56 #endif
58 #include <stdlib.h>
59 #include <stdio.h>
60 #include <string.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #ifdef HAVE_SYS_IOCTL_H
65 #include <sys/ioctl.h>
66 #endif
67 #ifdef HAVE_FCNTL_H
68 #include <fcntl.h>
69 #endif
70 #include <termios.h>
72 #include "lib/unixcompat.h" /* STDERR_FILENO */
74 #define LINUX_CONS_SAVER_C
75 #include "cons.saver.h"
77 /*** global variables ****************************************************************************/
79 /*** file scope macro definitions ****************************************************************/
81 /*** file scope type declarations ****************************************************************/
83 /*** forward declarations (file scope functions) *************************************************/
85 /*** file scope variables ************************************************************************/
87 /* --------------------------------------------------------------------------------------------- */
88 /*** file scope functions ************************************************************************/
89 /* --------------------------------------------------------------------------------------------- */
91 static void
92 send_contents (char *buffer, unsigned int columns, unsigned int rows)
94 unsigned char begin_line = 0, end_line = 0;
95 unsigned int lastline, lc_index, x;
96 unsigned char message, outbuf[1024], *p;
97 unsigned short bytes;
99 lc_index = 2 * rows * columns;
100 for (lastline = rows; lastline > 0; lastline--)
101 for (x = 0; x < columns; x++)
103 lc_index -= 2;
104 if (buffer[lc_index] != ' ')
105 goto out;
107 out:
109 message = CONSOLE_CONTENTS;
110 if (write (1, &message, 1) != 1)
111 return;
112 if (read (0, &begin_line, 1) != 1)
113 return;
114 if (read (0, &end_line, 1) != 1)
115 return;
116 if (begin_line > lastline)
117 begin_line = lastline;
118 if (end_line > lastline)
119 end_line = lastline;
121 lc_index = (end_line - begin_line) * columns;
122 bytes = lc_index;
124 if (write (1, &bytes, 2) != 2)
125 return;
126 if (bytes == 0)
127 return;
129 p = outbuf;
130 for (lc_index = 2 * begin_line * columns; lc_index < 2 * end_line * columns; lc_index += 2)
132 *p++ = buffer[lc_index];
133 if (p == outbuf + sizeof (outbuf))
135 if (write (1, outbuf, sizeof (outbuf)) != sizeof (outbuf))
136 return;
137 p = outbuf;
141 if (p != outbuf)
142 if (write (1, outbuf, p - outbuf) < (p - outbuf))
143 return;
146 /* --------------------------------------------------------------------------------------------- */
148 static void __attribute__((noreturn)) die (void)
150 unsigned char zero = 0;
151 ssize_t ret;
152 ret = write (1, &zero, 1);
153 (void) ret;
154 exit (3);
157 /* --------------------------------------------------------------------------------------------- */
158 /*** public functions ****************************************************************************/
159 /* --------------------------------------------------------------------------------------------- */
162 main (int argc, char **argv)
164 unsigned char action = 0, console_flag = 3;
165 int console_fd, vcsa_fd, console_minor, buffer_size;
166 struct stat st;
167 uid_t uid, euid;
168 char *buffer, *tty_name, console_name[16], vcsa_name[16];
169 const char *p, *q;
170 struct winsize winsz;
172 close (STDERR_FILENO);
174 if (argc != 2)
175 die ();
177 tty_name = argv[1];
178 if (strnlen (tty_name, 15) == 15 || strncmp (tty_name, "/dev/", 5))
179 die ();
181 setsid ();
182 uid = getuid ();
183 euid = geteuid ();
185 if (seteuid (uid) < 0)
186 die ();
187 console_fd = open (tty_name, O_RDONLY);
188 if (console_fd < 0)
189 die ();
190 if (fstat (console_fd, &st) < 0 || !S_ISCHR (st.st_mode))
191 die ();
192 #ifdef HAVE_STRUCT_STAT_ST_RDEV
193 if ((st.st_rdev & 0xff00) != 0x0400)
194 die ();
195 console_minor = (int) (st.st_rdev & 0x00ff);
196 #else
197 console_minor = 1; /* FIXME */
198 #endif
199 if (console_minor < 1 || console_minor > 63)
200 die ();
201 if (st.st_uid != uid)
202 die ();
204 switch (tty_name[5])
206 /* devfs */
207 case 'v':
208 p = "/dev/vc/%d";
209 q = "/dev/vcc/a%d";
210 break;
211 /* /dev/ttyN */
212 case 't':
213 p = "/dev/tty%d";
214 q = "/dev/vcsa%d";
215 break;
216 default:
217 die ();
220 snprintf (console_name, sizeof (console_name), p, console_minor);
221 if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
222 die ();
224 if (seteuid (euid) < 0)
225 die ();
227 snprintf (vcsa_name, sizeof (vcsa_name), q, console_minor);
228 vcsa_fd = open (vcsa_name, O_RDWR);
229 if (vcsa_fd < 0)
230 die ();
231 if (fstat (vcsa_fd, &st) < 0 || !S_ISCHR (st.st_mode))
232 die ();
234 if (seteuid (uid) < 0)
235 die ();
237 winsz.ws_col = winsz.ws_row = 0;
238 if (ioctl (console_fd, TIOCGWINSZ, &winsz) < 0
239 || winsz.ws_col <= 0 || winsz.ws_row <= 0 || winsz.ws_col >= 256 || winsz.ws_row >= 256)
240 die ();
242 buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
243 buffer = calloc (buffer_size, 1);
244 if (buffer == NULL)
245 die ();
247 if (write (1, &console_flag, 1) != 1)
248 die ();
250 while (console_flag && read (0, &action, 1) == 1)
252 switch (action)
254 case CONSOLE_DONE:
255 console_flag = 0;
256 continue;
257 case CONSOLE_SAVE:
258 if (seteuid (euid) < 0
259 || lseek (vcsa_fd, 0, 0) != 0
260 || fstat (console_fd, &st) < 0 || st.st_uid != uid
261 || read (vcsa_fd, buffer, buffer_size) != buffer_size
262 || fstat (console_fd, &st) < 0 || st.st_uid != uid)
263 memset (buffer, 0, buffer_size);
264 if (seteuid (uid) < 0)
265 die ();
266 break;
267 case CONSOLE_RESTORE:
268 if (seteuid (euid) >= 0
269 && lseek (vcsa_fd, 0, 0) == 0 && fstat (console_fd, &st) >= 0 && st.st_uid == uid)
270 if (write (vcsa_fd, buffer, buffer_size) != buffer_size)
271 die ();
272 if (seteuid (uid) < 0)
273 die ();
274 break;
275 case CONSOLE_CONTENTS:
276 send_contents (buffer + 4, winsz.ws_col, winsz.ws_row);
277 break;
278 default:
279 break;
282 if (write (1, &console_flag, 1) != 1)
283 die ();
286 exit (0);
289 /* --------------------------------------------------------------------------------------------- */