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