444 lines
13 KiB
C
444 lines
13 KiB
C
|
#if !defined( lint ) && !defined( SABER )
|
||
|
static const char sccsid[] = "@(#)vtlock_proc.c 1.2 00/08/30 xlockmore";
|
||
|
#endif
|
||
|
|
||
|
/* Copyright (c) R. Cohen-Scali, 1998. */
|
||
|
|
||
|
/*
|
||
|
* Permission to use, copy, modify, and distribute this software and its
|
||
|
* documentation for any purpose and without fee is hereby granted,
|
||
|
* provided that the above copyright notice appear in all copies and that
|
||
|
* both that copyright notice and this permission notice appear in
|
||
|
* supporting documentation.
|
||
|
*
|
||
|
* This file is provided AS IS with no warranties of any kind. The author
|
||
|
* shall have no liability with respect to the infringement of copyrights,
|
||
|
* trade secrets or any patents by this file or any part thereof. In no
|
||
|
* event will the author be liable for any lost revenue or profits or
|
||
|
* other special, indirect and consequential damages.
|
||
|
*
|
||
|
* <remi.cohenscali@pobox.com>
|
||
|
* 00/08/30: Eric Lassauge - updates for assorted compilation warnings
|
||
|
* 98/10/08: Eric Lassauge - vtlock_proc renamed from lockvt_proc.
|
||
|
* misc corrections.
|
||
|
*/
|
||
|
|
||
|
#include "xlock.h" /* lots of include come from here */
|
||
|
#ifdef USE_VTLOCK
|
||
|
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/ioctl.h>
|
||
|
#include <errno.h>
|
||
|
#if defined( __linux__ )
|
||
|
#include <linux/major.h>
|
||
|
#include <linux/tty.h>
|
||
|
#include <linux/vt.h>
|
||
|
#endif
|
||
|
|
||
|
/* Misc definitions to modify if not applicable on the current system */
|
||
|
#if defined( __linux__ ) && HAVE_DIRENT_H
|
||
|
#include <dirent.h> /* for alphasort() */
|
||
|
#define PROCDIR "/proc"
|
||
|
#define DEVDIR "/dev"
|
||
|
#define TTY DEVDIR "/tty%c"
|
||
|
#define CONSOLE DEVDIR "/console"
|
||
|
#define BASEVTNAME DEVDIR "/tty%d"
|
||
|
#define XPATH "/usr/X11R6/bin" /* default path of X server */
|
||
|
#define XNAME "X" /* X server name : mandatory ! */
|
||
|
#define MAX_VT 20
|
||
|
#else
|
||
|
#error Sorry ! You must adapt this file to your system !
|
||
|
#endif
|
||
|
|
||
|
/* This struct contains the vt tty refs used for comparison */
|
||
|
struct inode_ref {
|
||
|
unsigned short n;
|
||
|
char ref[MAXPATHLEN+1];
|
||
|
};
|
||
|
|
||
|
/* Static variables used to keep X device, inode and process */
|
||
|
static dev_t xdev =(dev_t)-1;
|
||
|
static ino_t xino =(ino_t)-1;
|
||
|
static pid_t xproc =(pid_t)-1;
|
||
|
static unsigned short xvt =(unsigned short)0;
|
||
|
static unsigned short othervt =(unsigned short)0;
|
||
|
|
||
|
/* Static variables to keep vt devices */
|
||
|
static int n_ttys = -1;
|
||
|
static struct inode_ref ttyinodes[MAX_NR_CONSOLES];
|
||
|
|
||
|
/* Prototypes */
|
||
|
static unsigned short get_active_vt(void);
|
||
|
static ino_t find_x(const char *, const char *, dev_t * );
|
||
|
static int proc_dir_select(const struct dirent *);
|
||
|
static pid_t find_x_proc(int, dev_t, ino_t);
|
||
|
static int find_tty_inodes(struct inode_ref *);
|
||
|
static int scan_x_fds(struct inode_ref *, int, pid_t);
|
||
|
|
||
|
/* use scan_dir from iostuff.c */
|
||
|
extern int scan_dir(const char *directoryname, struct dirent ***namelist,
|
||
|
int (*specify) (const struct dirent *),
|
||
|
int (*compare) (const void *, const void *));
|
||
|
|
||
|
extern int is_x_vt_active(int display_nr);
|
||
|
extern int set_x_vt_active(int display_nr);
|
||
|
extern int restore_vt_active(void);
|
||
|
|
||
|
/*
|
||
|
* get_active_vt
|
||
|
* -------------
|
||
|
* Find with ioctl on console what is the active vt number.
|
||
|
* The number found by this will be compared to the X vt number to
|
||
|
* find if vt locking is possible.
|
||
|
*/
|
||
|
static unsigned short
|
||
|
get_active_vt(void)
|
||
|
{
|
||
|
struct vt_stat vtstat;
|
||
|
int fd = -1;
|
||
|
|
||
|
fd = open( CONSOLE, O_RDONLY );
|
||
|
if ( fd == -1 ) return( (unsigned short) -1 );
|
||
|
if ( ioctl( fd, VT_GETSTATE,(void *)&vtstat ) == -1 )
|
||
|
{
|
||
|
close( fd );
|
||
|
return((unsigned short) -1 );
|
||
|
}
|
||
|
close( fd );
|
||
|
return vtstat.v_active;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* find_x
|
||
|
* ------
|
||
|
* Find X server executable file inode.
|
||
|
* The inode number found here will be used to find in the X process
|
||
|
* in the proc fs.
|
||
|
*/
|
||
|
static ino_t
|
||
|
find_x(const char *path, const char *name, dev_t *pxdev )
|
||
|
{
|
||
|
struct stat stbuf;
|
||
|
char xpath[MAXPATHLEN+1];
|
||
|
|
||
|
(void) sprintf( xpath, "%s/%s", path, name );
|
||
|
if ( stat( xpath, &stbuf ) != -1 ) {
|
||
|
(void) strcpy( xpath, name );
|
||
|
while ( S_ISLNK(stbuf.st_mode) ) {
|
||
|
char buf[MAXPATHLEN+1];
|
||
|
|
||
|
if (readlink(xpath, buf, MAXPATHLEN ) == -1 || ! *buf)
|
||
|
return( (ino_t) -1 );
|
||
|
|
||
|
/*
|
||
|
* Let's try to know if the path is absolute or relative
|
||
|
* It is absolute if it begin with '/',
|
||
|
* else is relative ,
|
||
|
* then we need to add the path given as argument
|
||
|
*/
|
||
|
if ( buf[0] != '/' )
|
||
|
(void) sprintf( xpath, "%s/%s", path, buf );
|
||
|
else
|
||
|
(void) strcpy( xpath, buf );
|
||
|
/* Stat linked file */
|
||
|
if ( stat( xpath, &stbuf ) == -1 ) return( (ino_t) -1 );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
return( (ino_t) -1 );
|
||
|
if ( pxdev ) *pxdev = stbuf.st_dev;
|
||
|
return stbuf.st_ino;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* proc_dir_select
|
||
|
* ---------------
|
||
|
* Callback called for each proc fs dir in order to select all
|
||
|
* processes directories. Only returns 1 for directory entries
|
||
|
* with a number [0->9].
|
||
|
*/
|
||
|
static int
|
||
|
proc_dir_select( const struct dirent *entry )
|
||
|
{
|
||
|
return( entry->d_name[0] >= '0' && entry->d_name[0] <= '9' );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* find_x_proc
|
||
|
* -----------
|
||
|
* This function scans the /proc dir in order to find the X process
|
||
|
* for the given display, knowing the X server file inode and device.
|
||
|
*/
|
||
|
static pid_t
|
||
|
find_x_proc(int disp_nr, dev_t lxdev, ino_t lxino)
|
||
|
{
|
||
|
/*static*/ char xdisp[10];
|
||
|
/*static*/ char xcmd_ref[MAXPATHLEN+1];
|
||
|
struct stat stbuf;
|
||
|
pid_t proc = -1;
|
||
|
struct dirent **namelist = NULL;
|
||
|
int curn = 0,names = 0;
|
||
|
int lencmd ;
|
||
|
|
||
|
/* These are the display string searched in X cmd running (e.g.: :1) */
|
||
|
/* and the searched value of the link (e.g.: "[0301]:286753") */
|
||
|
(void) sprintf( xdisp, ":%d", disp_nr );
|
||
|
(void) sprintf( xcmd_ref, "[%04x]:%ld", (int)lxdev, (long)lxino );
|
||
|
lencmd = strlen(xcmd_ref);
|
||
|
if ( stat( PROCDIR, &stbuf ) == -1 ) return( (pid_t)-1 );
|
||
|
namelist = (struct dirent **) malloc(sizeof (struct dirent *));
|
||
|
if ((names = scan_dir(PROCDIR, &namelist, proc_dir_select, alphasort)) == -1 )
|
||
|
{
|
||
|
free(namelist);
|
||
|
return( (pid_t)-1 );
|
||
|
}
|
||
|
while ( curn < names ) {
|
||
|
char pname[MAXPATHLEN+1];
|
||
|
char buf[MAXPATHLEN+1];
|
||
|
|
||
|
(void) sprintf( pname, PROCDIR "/%s/exe", namelist[curn]->d_name );
|
||
|
(void) memset((char *) buf, 0, sizeof (buf));
|
||
|
if ( readlink( pname, buf, MAXPATHLEN ) <= 0 ) {
|
||
|
/* This is unreadable, let's continue */
|
||
|
curn++;
|
||
|
continue;
|
||
|
}
|
||
|
/*
|
||
|
* If the strings are equals, we found an X process, but is it the one
|
||
|
* managing the wanted display ?
|
||
|
* We are going to try to know it by reading the command line used to
|
||
|
* invoke the server.
|
||
|
*/
|
||
|
if ( !strncmp( buf, xcmd_ref, lencmd ) ) {
|
||
|
char cmdlinepath[MAXPATHLEN+1];
|
||
|
char cmdlinebuf[1024]; /* 1k should be enough */
|
||
|
int cmdlinefd;
|
||
|
off_t cmdlinesz;
|
||
|
char *p;
|
||
|
|
||
|
proc =(pid_t)atoi( namelist[curn]->d_name );
|
||
|
(void) sprintf( cmdlinepath, PROCDIR "/%s/cmdline", namelist[curn]->d_name );
|
||
|
if ( ( cmdlinefd = open( cmdlinepath, O_RDONLY ) ) == -1 ) {
|
||
|
curn++;
|
||
|
continue;
|
||
|
}
|
||
|
/* Ask the kernel what it was (actually do ps)
|
||
|
* If stat'ed the cmdline proc file as a size of zero
|
||
|
* No means to dynamically allocate buffer !
|
||
|
*/
|
||
|
if ( ( cmdlinesz = read( cmdlinefd, cmdlinebuf, 1023 ) ) == -1) {
|
||
|
close( cmdlinefd );
|
||
|
curn++;
|
||
|
continue;
|
||
|
}
|
||
|
/*
|
||
|
* The command line proc file contains all command line args
|
||
|
* separated by NULL characters. We are going to replace all ^@
|
||
|
* with a space, then we'll searched for the display string
|
||
|
* (:0, :1, ..., :N). If a match is found, then we got the good
|
||
|
* process. If no match is found, then we can assume this is the
|
||
|
* good process only if we are searching for the display #0 manager
|
||
|
* (the default display). In other case the process is discarded.
|
||
|
*/
|
||
|
p = cmdlinebuf;
|
||
|
while ( p < cmdlinebuf+cmdlinesz ) {
|
||
|
if ( !*p ) *p = ' ';
|
||
|
p++;
|
||
|
}
|
||
|
close( cmdlinefd );
|
||
|
if ( strstr( cmdlinebuf, xdisp ) ) break;
|
||
|
else if ( !disp_nr )
|
||
|
break;
|
||
|
else
|
||
|
proc =(pid_t)-1;
|
||
|
}
|
||
|
curn++;
|
||
|
}
|
||
|
free(namelist);
|
||
|
return proc;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* find_tty_inodes
|
||
|
* ---------------
|
||
|
* This function finds all vt console dev and inode.
|
||
|
* Warning ! The dev is not the console major:minor
|
||
|
* but the filesystem's major:minor containing the special
|
||
|
* device file.
|
||
|
*/
|
||
|
static int
|
||
|
find_tty_inodes( struct inode_ref *inotab )
|
||
|
{
|
||
|
struct stat stbuf;
|
||
|
int ln_ttys = 0;
|
||
|
int ix = 0;
|
||
|
char name[MAXPATHLEN+1];
|
||
|
|
||
|
for ( ix = 1; ix < MAX_NR_CONSOLES; ix++ ) {
|
||
|
(void) sprintf( name, BASEVTNAME, ix );
|
||
|
if ( stat( name, &stbuf ) == -1 )
|
||
|
continue;
|
||
|
inotab[ln_ttys].n = ix;
|
||
|
(void) sprintf( inotab[ln_ttys].ref, "[%04x]:%ld", (int)stbuf.st_dev, stbuf.st_ino );
|
||
|
ln_ttys++;
|
||
|
}
|
||
|
return ln_ttys;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* scan_x_fds
|
||
|
* ----------
|
||
|
* This function scans all found process file descriptors
|
||
|
* to find a link towards a tty device special file.
|
||
|
*/
|
||
|
static int
|
||
|
scan_x_fds( struct inode_ref *inotab, int ln_ttys, pid_t proc )
|
||
|
{
|
||
|
char xfddir[MAXPATHLEN+1];
|
||
|
struct dirent **namelist=NULL;
|
||
|
int curn = 0;
|
||
|
|
||
|
(void) sprintf(xfddir, PROCDIR "/%d/fd", proc);
|
||
|
namelist = (struct dirent **) malloc(sizeof (struct dirent *));
|
||
|
if (scan_dir(xfddir, &namelist, NULL, alphasort) == -1) {
|
||
|
free(namelist);
|
||
|
return 0;
|
||
|
}
|
||
|
while ( namelist[curn] ) {
|
||
|
char linkname[MAXPATHLEN+1];
|
||
|
char linkref[MAXPATHLEN+1];
|
||
|
struct stat stbuf;
|
||
|
int ix;
|
||
|
|
||
|
(void) sprintf( linkname, "%s/%s", xfddir, namelist[curn]->d_name );
|
||
|
if ( stat( linkname, &stbuf ) == -1 ) {
|
||
|
/* If cannot stat it, just discard it */
|
||
|
curn++;
|
||
|
continue;
|
||
|
}
|
||
|
if ( !S_ISDIR(stbuf.st_mode) ) {
|
||
|
/*
|
||
|
* Let's read the link to get the file device and inode
|
||
|
* (e.g.: [0301]:6203)
|
||
|
*/
|
||
|
(void) memset((char *) linkref, 0, sizeof (linkref));
|
||
|
if ( readlink( linkname, linkref, MAXPATHLEN ) <= 0 ) {
|
||
|
curn++;
|
||
|
continue;
|
||
|
}
|
||
|
for ( ix = 0; ix < ln_ttys; ix++ )
|
||
|
{
|
||
|
if ( !strncmp( linkref, inotab[ix].ref, strlen( inotab[ix].ref ) ) )
|
||
|
{
|
||
|
free(namelist);
|
||
|
return inotab[ix].n;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
curn++;
|
||
|
}
|
||
|
free(namelist);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* is_x_vt_active
|
||
|
* --------------
|
||
|
* This function is the one called from vtlock.c and which tells
|
||
|
* if the X vt is active or not.
|
||
|
* If the X vt is active it returns 1 else 0.
|
||
|
* -1 is returned in case of errno or if cannot stat.
|
||
|
*/
|
||
|
int
|
||
|
is_x_vt_active(int display_nr)
|
||
|
{
|
||
|
int active_vt = 0;
|
||
|
char *path = getenv( "PATH" );
|
||
|
char *envtokenizer =(char *)NULL;
|
||
|
struct stat stbuf;
|
||
|
|
||
|
/* The active VT */
|
||
|
active_vt = get_active_vt();
|
||
|
if ( xino == (ino_t)-1 )
|
||
|
if (stat( XPATH, &stbuf ) == -1 ||
|
||
|
(xino = find_x( XPATH, XNAME, &xdev )) == (ino_t)-1 ) {
|
||
|
/* No executable at the default location */
|
||
|
/* Let's try with $PATH */
|
||
|
if ( !path ) return -1;
|
||
|
envtokenizer = strtok( path, ":" );
|
||
|
while ( envtokenizer ) {
|
||
|
if ( stat( envtokenizer, &stbuf ) != -1 )
|
||
|
if ( ( xino = find_x( envtokenizer, XNAME, &xdev ) ) != (ino_t)-1 )
|
||
|
break;
|
||
|
envtokenizer = strtok( (char *)NULL, ":" );
|
||
|
}
|
||
|
if ( !envtokenizer ) return -1;
|
||
|
}
|
||
|
if ((xproc ==(pid_t)-1 ) &&
|
||
|
(xproc = find_x_proc(display_nr, xdev, xino)) == (pid_t)-1)
|
||
|
return -1;
|
||
|
if ((n_ttys == -1) &&
|
||
|
(n_ttys = find_tty_inodes(ttyinodes))== 0)
|
||
|
return -1;
|
||
|
if ( ! xvt && ( xvt = scan_x_fds( ttyinodes, n_ttys, xproc ) ) == 0 ) return -1;
|
||
|
return(active_vt == xvt);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* set_x_vt_active
|
||
|
* ---------------
|
||
|
* This is the core function. It check if the VT of the X server managing
|
||
|
* display number <display_nr> is active.
|
||
|
*/
|
||
|
int
|
||
|
set_x_vt_active(int display_nr)
|
||
|
{
|
||
|
int fd = -1;
|
||
|
|
||
|
if ( !othervt ) othervt = get_active_vt();
|
||
|
if ( !xvt ) (void)is_x_vt_active( display_nr );
|
||
|
fd = open( CONSOLE, O_RDONLY );
|
||
|
if ( fd == -1 ) return( -1 );
|
||
|
if ( ioctl( fd, VT_ACTIVATE, xvt ) == -1 ) {
|
||
|
close( fd );
|
||
|
return( -1 );
|
||
|
}
|
||
|
if ( ioctl( fd, VT_WAITACTIVE, xvt ) == -1 ) {
|
||
|
close( fd );
|
||
|
return( -1 );
|
||
|
}
|
||
|
close( fd );
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* restore_vt_active
|
||
|
* -----------------
|
||
|
* This function revert the work of the previous one. Actually it restores
|
||
|
* the VT which was active when we locked.
|
||
|
*/
|
||
|
int
|
||
|
restore_vt_active(void)
|
||
|
{
|
||
|
int fd = -1;
|
||
|
|
||
|
if ( !othervt ) return 0;
|
||
|
fd = open( CONSOLE, O_RDONLY );
|
||
|
if ( fd == -1 ) return( -1 );
|
||
|
if ( ioctl( fd, VT_ACTIVATE, othervt ) == -1 ) {
|
||
|
close( fd );
|
||
|
return( -1 );
|
||
|
}
|
||
|
if ( ioctl( fd, VT_WAITACTIVE, xvt ) == -1 ) {
|
||
|
/* We can achieve that vt has switched */
|
||
|
othervt =(unsigned short)0;
|
||
|
close( fd );
|
||
|
return( -1 );
|
||
|
}
|
||
|
othervt =(unsigned short)0;
|
||
|
close( fd );
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|