1 #if 0
2 static const char sccsid[] = "@(#)vtlock_proc.c 1.2 00/08/30 xlockmore";
3 #endif
4
5 /* Copyright (c) R. Cohen-Scali, 1998. */
6
7 /*
8 * Permission to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted,
10 * provided that the above copyright notice appear in all copies and that
11 * both that copyright notice and this permission notice appear in
12 * supporting documentation.
13 *
14 * This file is provided AS IS with no warranties of any kind. The author
15 * shall have no liability with respect to the infringement of copyrights,
16 * trade secrets or any patents by this file or any part thereof. In no
17 * event will the author be liable for any lost revenue or profits or
18 * other special, indirect and consequential damages.
19 *
20 * <remi.cohenscali@pobox.com>
21 * 00/08/30: Eric Lassauge - updates for assorted compilation warnings
22 * 98/10/08: Eric Lassauge - vtlock_proc renamed from lockvt_proc.
23 * misc corrections.
24 */
25
26 #include "xlock.h" /* lots of include come from here */
27 #ifdef USE_VTLOCK
28
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <sys/ioctl.h>
32 #include <errno.h>
33 #if defined( __linux__ )
34 #include <linux/major.h>
35 #include <linux/tty.h>
36 #include <linux/vt.h>
37 #endif
38
39 /* Misc definitions to modify if not applicable on the current system */
40 #if defined( __linux__ ) && HAVE_DIRENT_H
41 #include <dirent.h> /* for alphasort() */
42 #define PROCDIR "/proc"
43 #define DEVDIR "/dev"
44 #define TTY DEVDIR "/tty%c"
45 #define CONSOLE DEVDIR "/console"
46 #define BASEVTNAME DEVDIR "/tty%d"
47 #define XPATH "/usr/X11R6/bin" /* default path of X server */
48 #define XNAME "X" /* X server name : mandatory ! */
49 #define MAX_VT 20
50 #else
51 #error Sorry ! You must adapt this file to your system !
52 #endif
53
54 /* This struct contains the vt tty refs used for comparison */
55 struct inode_ref {
56 unsigned short n;
57 char ref[MAXPATHLEN+1];
58 };
59
60 /* Static variables used to keep X device, inode and process */
61 static dev_t xdev =(dev_t)-1;
62 static ino_t xino =(ino_t)-1;
63 static pid_t xproc =(pid_t)-1;
64 static unsigned short xvt =(unsigned short)0;
65 static unsigned short othervt =(unsigned short)0;
66
67 /* Static variables to keep vt devices */
68 static int n_ttys = -1;
69 static struct inode_ref ttyinodes[MAX_NR_CONSOLES];
70
71 /* Prototypes */
72 static unsigned short get_active_vt(void);
73 static ino_t find_x(const char *, const char *, dev_t * );
74 static int proc_dir_select(const struct dirent *);
75 static pid_t find_x_proc(int, dev_t, ino_t);
76 static int find_tty_inodes(struct inode_ref *);
77 static int scan_x_fds(struct inode_ref *, int, pid_t);
78
79 /* use scan_dir from iostuff.c */
80 extern int scan_dir(const char *directoryname, struct dirent ***namelist,
81 int (*specify) (const struct dirent *),
82 int (*compare) (const void *, const void *));
83
84 extern int is_x_vt_active(int display_nr);
85 extern int set_x_vt_active(int display_nr);
86 extern int restore_vt_active(void);
87
88 /*
89 * get_active_vt
90 * -------------
91 * Find with ioctl on console what is the active vt number.
92 * The number found by this will be compared to the X vt number to
93 * find if vt locking is possible.
94 */
95 static unsigned short
get_active_vt(void)96 get_active_vt(void)
97 {
98 struct vt_stat vtstat;
99 int fd = -1;
100
101 fd = open( CONSOLE, O_RDONLY );
102 if ( fd == -1 ) return( (unsigned short) -1 );
103 if ( ioctl( fd, VT_GETSTATE,(void *)&vtstat ) == -1 )
104 {
105 close( fd );
106 return((unsigned short) -1 );
107 }
108 close( fd );
109 return vtstat.v_active;
110 }
111
112 /*
113 * find_x
114 * ------
115 * Find X server executable file inode.
116 * The inode number found here will be used to find in the X process
117 * in the proc fs.
118 */
119 static ino_t
find_x(const char * path,const char * name,dev_t * pxdev)120 find_x(const char *path, const char *name, dev_t *pxdev )
121 {
122 struct stat stbuf;
123 char xpath[MAXPATHLEN+1];
124
125 (void) sprintf( xpath, "%s/%s", path, name );
126 if ( stat( xpath, &stbuf ) != -1 ) {
127 (void) strcpy( xpath, name );
128 while ( S_ISLNK(stbuf.st_mode) ) {
129 char buf[MAXPATHLEN+1];
130
131 if (readlink(xpath, buf, MAXPATHLEN ) == -1 || ! *buf)
132 return( (ino_t) -1 );
133
134 /*
135 * Let's try to know if the path is absolute or relative
136 * It is absolute if it begin with '/',
137 * else is relative ,
138 * then we need to add the path given as argument
139 */
140 if ( buf[0] != '/' )
141 (void) sprintf( xpath, "%s/%s", path, buf );
142 else
143 (void) strcpy( xpath, buf );
144 /* Stat linked file */
145 if ( stat( xpath, &stbuf ) == -1 ) return( (ino_t) -1 );
146 }
147 }
148 else
149 return( (ino_t) -1 );
150 if ( pxdev ) *pxdev = stbuf.st_dev;
151 return stbuf.st_ino;
152 }
153
154 /*
155 * proc_dir_select
156 * ---------------
157 * Callback called for each proc fs dir in order to select all
158 * processes directories. Only returns 1 for directory entries
159 * with a number [0->9].
160 */
161 static int
proc_dir_select(const struct dirent * entry)162 proc_dir_select( const struct dirent *entry )
163 {
164 return( entry->d_name[0] >= '0' && entry->d_name[0] <= '9' );
165 }
166
167 /*
168 * find_x_proc
169 * -----------
170 * This function scans the /proc dir in order to find the X process
171 * for the given display, knowing the X server file inode and device.
172 */
173 static pid_t
find_x_proc(int disp_nr,dev_t lxdev,ino_t lxino)174 find_x_proc(int disp_nr, dev_t lxdev, ino_t lxino)
175 {
176 /*static*/ char xdisp[10];
177 /*static*/ char xcmd_ref[MAXPATHLEN+1];
178 struct stat stbuf;
179 pid_t proc = -1;
180 struct dirent **namelist = NULL;
181 int curn = 0,names = 0;
182 int lencmd ;
183
184 /* These are the display string searched in X cmd running (e.g.: :1) */
185 /* and the searched value of the link (e.g.: "[0301]:286753") */
186 (void) sprintf( xdisp, ":%d", disp_nr );
187 (void) sprintf( xcmd_ref, "[%04x]:%ld", (int)lxdev, (long)lxino );
188 lencmd = strlen(xcmd_ref);
189 if ( stat( PROCDIR, &stbuf ) == -1 ) return( (pid_t)-1 );
190 namelist = (struct dirent **) malloc(sizeof (struct dirent *));
191 if ((names = scan_dir(PROCDIR, &namelist, proc_dir_select, alphasort)) == -1 )
192 {
193 free(namelist);
194 return( (pid_t)-1 );
195 }
196 while ( curn < names ) {
197 char pname[MAXPATHLEN+1];
198 char buf[MAXPATHLEN+1];
199
200 (void) sprintf( pname, PROCDIR "/%s/exe", namelist[curn]->d_name );
201 (void) memset((char *) buf, 0, sizeof (buf));
202 if ( readlink( pname, buf, MAXPATHLEN ) <= 0 ) {
203 /* This is unreadable, let's continue */
204 curn++;
205 continue;
206 }
207 /*
208 * If the strings are equals, we found an X process, but is it the one
209 * managing the wanted display ?
210 * We are going to try to know it by reading the command line used to
211 * invoke the server.
212 */
213 if ( !strncmp( buf, xcmd_ref, lencmd ) ) {
214 char cmdlinepath[MAXPATHLEN+1];
215 char cmdlinebuf[1024]; /* 1k should be enough */
216 int cmdlinefd;
217 off_t cmdlinesz;
218 char *p;
219
220 proc =(pid_t)atoi( namelist[curn]->d_name );
221 (void) sprintf( cmdlinepath, PROCDIR "/%s/cmdline", namelist[curn]->d_name );
222 if ( ( cmdlinefd = open( cmdlinepath, O_RDONLY ) ) == -1 ) {
223 curn++;
224 continue;
225 }
226 /* Ask the kernel what it was (actually do ps)
227 * If stat'ed the cmdline proc file as a size of zero
228 * No means to dynamically allocate buffer !
229 */
230 if ( ( cmdlinesz = read( cmdlinefd, cmdlinebuf, 1023 ) ) == -1) {
231 close( cmdlinefd );
232 curn++;
233 continue;
234 }
235 /*
236 * The command line proc file contains all command line args
237 * separated by NULL characters. We are going to replace all ^@
238 * with a space, then we'll searched for the display string
239 * (:0, :1, ..., :N). If a match is found, then we got the good
240 * process. If no match is found, then we can assume this is the
241 * good process only if we are searching for the display #0 manager
242 * (the default display). In other case the process is discarded.
243 */
244 p = cmdlinebuf;
245 while ( p < cmdlinebuf+cmdlinesz ) {
246 if ( !*p ) *p = ' ';
247 p++;
248 }
249 close( cmdlinefd );
250 if ( strstr( cmdlinebuf, xdisp ) ) break;
251 else if ( !disp_nr )
252 break;
253 else
254 proc =(pid_t)-1;
255 }
256 curn++;
257 }
258 free(namelist);
259 return proc;
260 }
261
262 /*
263 * find_tty_inodes
264 * ---------------
265 * This function finds all vt console dev and inode.
266 * Warning ! The dev is not the console major:minor
267 * but the filesystem's major:minor containing the special
268 * device file.
269 */
270 static int
find_tty_inodes(struct inode_ref * inotab)271 find_tty_inodes( struct inode_ref *inotab )
272 {
273 struct stat stbuf;
274 int ln_ttys = 0;
275 int ix = 0;
276 char name[MAXPATHLEN+1];
277
278 for ( ix = 1; ix < MAX_NR_CONSOLES; ix++ ) {
279 (void) sprintf( name, BASEVTNAME, ix );
280 if ( stat( name, &stbuf ) == -1 )
281 continue;
282 inotab[ln_ttys].n = ix;
283 (void) sprintf( inotab[ln_ttys].ref, "[%04x]:%ld", (int)stbuf.st_dev, stbuf.st_ino );
284 ln_ttys++;
285 }
286 return ln_ttys;
287 }
288
289 /*
290 * scan_x_fds
291 * ----------
292 * This function scans all found process file descriptors
293 * to find a link towards a tty device special file.
294 */
295 static int
scan_x_fds(struct inode_ref * inotab,int ln_ttys,pid_t proc)296 scan_x_fds( struct inode_ref *inotab, int ln_ttys, pid_t proc )
297 {
298 char xfddir[MAXPATHLEN+1];
299 struct dirent **namelist=NULL;
300 int curn = 0;
301
302 (void) sprintf(xfddir, PROCDIR "/%d/fd", proc);
303 namelist = (struct dirent **) malloc(sizeof (struct dirent *));
304 if (scan_dir(xfddir, &namelist, NULL, alphasort) == -1) {
305 free(namelist);
306 return 0;
307 }
308 while ( namelist[curn] ) {
309 char linkname[MAXPATHLEN+1];
310 char linkref[MAXPATHLEN+1];
311 struct stat stbuf;
312 int ix;
313
314 (void) sprintf( linkname, "%s/%s", xfddir, namelist[curn]->d_name );
315 if ( stat( linkname, &stbuf ) == -1 ) {
316 /* If cannot stat it, just discard it */
317 curn++;
318 continue;
319 }
320 if ( !S_ISDIR(stbuf.st_mode) ) {
321 /*
322 * Let's read the link to get the file device and inode
323 * (e.g.: [0301]:6203)
324 */
325 (void) memset((char *) linkref, 0, sizeof (linkref));
326 if ( readlink( linkname, linkref, MAXPATHLEN ) <= 0 ) {
327 curn++;
328 continue;
329 }
330 for ( ix = 0; ix < ln_ttys; ix++ )
331 {
332 if ( !strncmp( linkref, inotab[ix].ref, strlen( inotab[ix].ref ) ) )
333 {
334 free(namelist);
335 return inotab[ix].n;
336 }
337 }
338 }
339 curn++;
340 }
341 free(namelist);
342 return 0;
343 }
344
345 /*
346 * is_x_vt_active
347 * --------------
348 * This function is the one called from vtlock.c and which tells
349 * if the X vt is active or not.
350 * If the X vt is active it returns 1 else 0.
351 * -1 is returned in case of errno or if cannot stat.
352 */
353 int
is_x_vt_active(int display_nr)354 is_x_vt_active(int display_nr)
355 {
356 int active_vt = 0;
357 char *path = getenv( "PATH" );
358 char *envtokenizer =(char *)NULL;
359 struct stat stbuf;
360
361 /* The active VT */
362 active_vt = get_active_vt();
363 if ( xino == (ino_t)-1 )
364 if (stat( XPATH, &stbuf ) == -1 ||
365 (xino = find_x( XPATH, XNAME, &xdev )) == (ino_t)-1 ) {
366 /* No executable at the default location */
367 /* Let's try with $PATH */
368 if ( !path ) return -1;
369 envtokenizer = strtok( path, ":" );
370 while ( envtokenizer ) {
371 if ( stat( envtokenizer, &stbuf ) != -1 )
372 if ( ( xino = find_x( envtokenizer, XNAME, &xdev ) ) != (ino_t)-1 )
373 break;
374 envtokenizer = strtok( (char *)NULL, ":" );
375 }
376 if ( !envtokenizer ) return -1;
377 }
378 if ((xproc ==(pid_t)-1 ) &&
379 (xproc = find_x_proc(display_nr, xdev, xino)) == (pid_t)-1)
380 return -1;
381 if ((n_ttys == -1) &&
382 (n_ttys = find_tty_inodes(ttyinodes))== 0)
383 return -1;
384 if ( ! xvt && ( xvt = scan_x_fds( ttyinodes, n_ttys, xproc ) ) == 0 ) return -1;
385 return(active_vt == xvt);
386 }
387
388 /*
389 * set_x_vt_active
390 * ---------------
391 * This is the core function. It check if the VT of the X server managing
392 * display number <display_nr> is active.
393 */
394 int
set_x_vt_active(int display_nr)395 set_x_vt_active(int display_nr)
396 {
397 int fd = -1;
398
399 if ( !othervt ) othervt = get_active_vt();
400 if ( !xvt ) (void)is_x_vt_active( display_nr );
401 fd = open( CONSOLE, O_RDONLY );
402 if ( fd == -1 ) return( -1 );
403 if ( ioctl( fd, VT_ACTIVATE, xvt ) == -1 ) {
404 close( fd );
405 return( -1 );
406 }
407 if ( ioctl( fd, VT_WAITACTIVE, xvt ) == -1 ) {
408 close( fd );
409 return( -1 );
410 }
411 close( fd );
412 return 0;
413 }
414
415 /*
416 * restore_vt_active
417 * -----------------
418 * This function revert the work of the previous one. Actually it restores
419 * the VT which was active when we locked.
420 */
421 int
restore_vt_active(void)422 restore_vt_active(void)
423 {
424 int fd = -1;
425
426 if ( !othervt ) return 0;
427 fd = open( CONSOLE, O_RDONLY );
428 if ( fd == -1 ) return( -1 );
429 if ( ioctl( fd, VT_ACTIVATE, othervt ) == -1 ) {
430 close( fd );
431 return( -1 );
432 }
433 if ( ioctl( fd, VT_WAITACTIVE, xvt ) == -1 ) {
434 /* We can achieve that vt has switched */
435 othervt =(unsigned short)0;
436 close( fd );
437 return( -1 );
438 }
439 othervt =(unsigned short)0;
440 close( fd );
441 return 0;
442 }
443 #endif
444