Add src/execute.c:execute_external_editor_or_viewer() function.
[midnight-commander.git] / src / consaver / cons.saver.c
blob234850e8a477cf489eb1b147881dd203f741877e
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, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
15 2006, 2007, 2011
16 The Free Software Foundation, Inc.
18 This file is part of the Midnight Commander.
20 The Midnight Commander is free software: you can redistribute it
21 and/or modify it under the terms of the GNU General Public License as
22 published by the Free Software Foundation, either version 3 of the License,
23 or (at your option) any later version.
25 The Midnight Commander is distributed in the hope that it will be useful,
26 but WITHOUT ANY WARRANTY; without even the implied warranty of
27 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 GNU General Public License for more details.
30 You should have received a copy of the GNU General Public License
31 along with this program. If not, see <http://www.gnu.org/licenses/>.
34 /* This code does _not_ need to be setuid root. However, it needs
35 read/write access to /dev/vcsa* (which is priviledged
36 operation). You should create user vcsa, make cons.saver setuid
37 user vcsa, and make all vcsa's owned by user vcsa.
39 Seeing other peoples consoles is bad thing, but believe me, full
40 root is even worse. */
42 /** \file cons.saver.c
43 * \brief Source: general purpose Linux console screen save/restore server
45 * This code does _not_ need to be setuid root. However, it needs
46 * read/write access to /dev/vcsa* (which is priviledged
47 * operation). You should create user vcsa, make cons.saver setuid
48 * user vcsa, and make all vcsa's owned by user vcsa.
49 * Seeing other peoples consoles is bad thing, but believe me, full
50 * root is even worse.
53 #include <config.h>
55 #ifndef _GNU_SOURCE
56 #define _GNU_SOURCE
57 #endif
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include <string.h>
63 #include <sys/types.h>
64 #include <sys/stat.h>
65 #ifdef HAVE_SYS_IOCTL_H
66 #include <sys/ioctl.h>
67 #endif
68 #include <fcntl.h>
69 #include <termios.h>
70 #include <unistd.h>
72 #define LINUX_CONS_SAVER_C
73 #include "cons.saver.h"
75 /*** global variables ****************************************************************************/
77 /*** file scope macro definitions ****************************************************************/
79 /*** file scope type declarations ****************************************************************/
81 /*** file scope variables ************************************************************************/
83 /*** file scope functions ************************************************************************/
84 /* --------------------------------------------------------------------------------------------- */
86 static void
87 send_contents (char *buffer, unsigned int columns, unsigned int rows)
89 unsigned char begin_line = 0, end_line = 0;
90 unsigned int lastline, lc_index, x;
91 unsigned char message, outbuf[1024], *p;
92 unsigned short bytes;
94 lc_index = 2 * rows * columns;
95 for (lastline = rows; lastline > 0; lastline--)
96 for (x = 0; x < columns; x++)
98 lc_index -= 2;
99 if (buffer[lc_index] != ' ')
100 goto out;
102 out:
104 message = CONSOLE_CONTENTS;
105 if (write (1, &message, 1) != 1)
106 return;
107 if (read (0, &begin_line, 1) != 1)
108 return;
109 if (read (0, &end_line, 1) != 1)
110 return;
111 if (begin_line > lastline)
112 begin_line = lastline;
113 if (end_line > lastline)
114 end_line = lastline;
116 lc_index = (end_line - begin_line) * columns;
117 bytes = lc_index;
119 if (write (1, &bytes, 2) != 2)
120 return;
121 if (bytes == 0)
122 return;
124 p = outbuf;
125 for (lc_index = 2 * begin_line * columns; lc_index < 2 * end_line * columns; lc_index += 2)
127 *p++ = buffer[lc_index];
128 if (p == outbuf + sizeof (outbuf))
130 if (write (1, outbuf, sizeof (outbuf)) != sizeof (outbuf))
131 return;
132 p = outbuf;
136 if (p != outbuf)
137 if (write (1, outbuf, p - outbuf) < (p - outbuf))
138 return;
141 /* --------------------------------------------------------------------------------------------- */
143 static void __attribute__ ((noreturn)) die (void)
145 unsigned char zero = 0;
146 ssize_t ret;
147 ret = write (1, &zero, 1);
148 (void) ret;
149 exit (3);
152 /* --------------------------------------------------------------------------------------------- */
153 /*** public functions ****************************************************************************/
154 /* --------------------------------------------------------------------------------------------- */
157 main (int argc, char **argv)
159 unsigned char action = 0, console_flag = 3;
160 int console_fd, vcsa_fd, console_minor, buffer_size;
161 struct stat st;
162 uid_t uid, euid;
163 char *buffer, *tty_name, console_name[16], vcsa_name[16];
164 const char *p, *q;
165 struct winsize winsz;
167 close (2);
169 if (argc != 2)
170 die ();
172 tty_name = argv[1];
173 if (strnlen (tty_name, 15) == 15 || strncmp (tty_name, "/dev/", 5))
174 die ();
176 setsid ();
177 uid = getuid ();
178 euid = geteuid ();
180 if (seteuid (uid) < 0)
181 die ();
182 console_fd = open (tty_name, O_RDONLY);
183 if (console_fd < 0)
184 die ();
185 if (fstat (console_fd, &st) < 0 || !S_ISCHR (st.st_mode))
186 die ();
187 if ((st.st_rdev & 0xff00) != 0x0400)
188 die ();
189 console_minor = (int) (st.st_rdev & 0x00ff);
190 if (console_minor < 1 || console_minor > 63)
191 die ();
192 if (st.st_uid != uid)
193 die ();
195 switch (tty_name[5])
197 /* devfs */
198 case 'v':
199 p = "/dev/vc/%d";
200 q = "/dev/vcc/a%d";
201 break;
202 /* /dev/ttyN */
203 case 't':
204 p = "/dev/tty%d";
205 q = "/dev/vcsa%d";
206 break;
207 default:
208 die ();
209 break;
212 snprintf (console_name, sizeof (console_name), p, console_minor);
213 if (strncmp (console_name, tty_name, sizeof (console_name)) != 0)
214 die ();
216 if (seteuid (euid) < 0)
217 die ();
219 snprintf (vcsa_name, sizeof (vcsa_name), q, console_minor);
220 vcsa_fd = open (vcsa_name, O_RDWR);
221 if (vcsa_fd < 0)
222 die ();
223 if (fstat (vcsa_fd, &st) < 0 || !S_ISCHR (st.st_mode))
224 die ();
226 if (seteuid (uid) < 0)
227 die ();
229 winsz.ws_col = winsz.ws_row = 0;
230 if (ioctl (console_fd, TIOCGWINSZ, &winsz) < 0
231 || winsz.ws_col <= 0 || winsz.ws_row <= 0 || winsz.ws_col >= 256 || winsz.ws_row >= 256)
232 die ();
234 buffer_size = 4 + 2 * winsz.ws_col * winsz.ws_row;
235 buffer = calloc (buffer_size, 1);
236 if (buffer == NULL)
237 die ();
239 if (write (1, &console_flag, 1) != 1)
240 die ();
242 while (console_flag && read (0, &action, 1) == 1)
244 switch (action)
246 case CONSOLE_DONE:
247 console_flag = 0;
248 continue;
249 case CONSOLE_SAVE:
250 if (seteuid (euid) < 0
251 || lseek (vcsa_fd, 0, 0) != 0
252 || fstat (console_fd, &st) < 0 || st.st_uid != uid
253 || read (vcsa_fd, buffer, buffer_size) != buffer_size
254 || fstat (console_fd, &st) < 0 || st.st_uid != uid)
255 memset (buffer, 0, buffer_size);
256 if (seteuid (uid) < 0)
257 die ();
258 break;
259 case CONSOLE_RESTORE:
260 if (seteuid (euid) >= 0
261 && lseek (vcsa_fd, 0, 0) == 0 && fstat (console_fd, &st) >= 0 && st.st_uid == uid)
262 if (write (vcsa_fd, buffer, buffer_size) != buffer_size)
263 die ();
264 if (seteuid (uid) < 0)
265 die ();
266 break;
267 case CONSOLE_CONTENTS:
268 send_contents (buffer + 4, winsz.ws_col, winsz.ws_row);
269 break;
272 if (write (1, &console_flag, 1) != 1)
273 die ();
276 exit (0);
279 /* --------------------------------------------------------------------------------------------- */