xenocara/app/xlockmore/xlock/vtlock_proc.c
2006-11-26 11:07:42 +00:00

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