[comp.unix.ultrix] The fix! Now in C

bph@buengc.BU.EDU (Blair P. Houghton) (02/25/90)

In article <5404@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:
>In article <77334@tut.cis.ohio-state.edu> muhanna@stock.cob.ohio-state.edu (Waleed A. Muhanna) writes:
>>What concerns me the most, however, is the fact that the virtual
>>(memory) size of the X server process keeps growing, sometimes to
>>the point where I have to reboot to reclaim swap space.
>
>You don't have to reboot.  Just use a 'ps -aux' to get the
>pid of the Xqdsg or Xqvsm process, then use 'kill -KILL pid'.

What ho! you say you have to be the superuser to do this?
Not if you install it as a setuid-root shell script!

[Much hissing and throwing of tomatoes and other BSD bugs, deleted to
save our PG-13 rating... :-)]

So here it is as a C program.  Unshar it, as superuser type 'make install',
and tell the kiddies they can clobber themselves at will.

Actually, it'd probably behoove you to look over the README
and adjust a few things to make it run on your system.
It's perhaps a bit provincially conformed to a uVAX-II/GPX
or vs2000, running Ultrix 3.1/UWS 2.1, and it assumes that
you're using Xqdsg or Xqvsm, rather than the MIT Xqdss, and
that you have the login window named either 'ttyv0' or 'ttyv1'.

Feel free to email me the diff's.

				--Blair
				  "Hey, at least it does two-head
				   and comes with a manual page..."

------------------ Klip and Kill -------------------------------
#!/bin/sh
#
# This is a shell archive.  To extract its contents,
# execute this file with /bin/sh to create the files:
# Makefile	    README		killXserver.c	    killXserver.l
#
# This shell archive created: Sat Feb 24 18:04:37 EST 1990
#
echo "extracting file README"
sed -e 's/^X//' <<\*EOF > README
XThe only gain from installing killXserver is that *any* user
Xis given the authority to kill-restart the X server, so
Xthat superusers aren't forced to keep rebooting
Xworkstations or su'ing just to kill X servers.  This is a
Xsignificant problem in a cluster of stations that have hundreds
Xof users and limited virtual memory, but if you're just
Xmaintaining your own machine, killXserver is overkill.
X
XALTERNATIVE:
X	Don't install killXserver.
X	Become root.  Check the size of the X server
X	process and get its pid ('ps aux' shows both,
X	and the server name).  If it's too big,
X	use 'kill -KILL pid'.
X
XMAKING:
X	'make install' cc's with -O, mv's the binary
X	to /usr/local/bin, strip's it, chmod's it 4755,
X	then cp's the manual page to /usr/man/manl
X	(that's an ell, not a one).
X
XDEBUGGING:
X	No DEBUG flags.  Cc with -g and use dbx(1) as
X	long as you have it.
X
XPoints to watch out for:
X
XTTY NAMES:
X	assumes you use "ttyv0" or "ttyv1".  These are
X	hardcoded in several places, and not as strings
X	but as sets of char's.  In fact, if the string
X	doesn't start with "ttyv", much of the structure
X	would have to change, since the display is encoded
X	as a single char (either '0' or '1').
X
XSERVER NAMES:
X	assumes you use "Xqdsg" or "Xqvsm".  These are
X	hardcoded in several places, and not as strings
X	but as sets of char's.
X
XENVIRONMENT:
X	DISPLAY must be set.  There are no options.
X
XLS -L:
X	Because stat(2) refused to let me look at a tty
X	device (I don't know why), I had to get the output
X	of 'ls -l' in order to verify ownership of the tty.
X	The username is found at column 14 of the returned
X	line.  This number is hardcoded, and may need to
X	be changed for (very) strange BSD implementations.
X
XPOPEN, PCLOSE:
X	popen(3) is available in Ultrix, and most other
X	BSD's, but I don't think it's POSIX.  I'm certain
X	it isn't X3.159.  Fixing this should require
X	restructuring with file-descriptor wizardry and
X	a funky call to system(3), or a temporary file.
X
XANSI:
X	Not even close.
X
XBasically, it works here, and will fit the bill for people
Xwho've been having this problem enough to ask for help
Xin comp.unix.ultrix.
X
X				--Blair
X				  "Ain't Ultrix Grand?"
*EOF
if [ `wc -c <README` -ne     2151 ]
then
	echo "lengths do not match -- Bad Copy of README"
fi
echo "extracting file Makefile"
sed -e 's/^X//' <<\*EOF > Makefile
X#! /bin/make
X#
X#  Makefile for killXserver, the superuser's helper.
X#
XINSBIN = /usr/local/bin/killXserver
XINSMAN = /usr/man/manl/killXserver.l
X#  These are ells ---^-------------^
XMANPG = killXserver.l
X#  So is this ------^
X
XCFLAGS = -O
X
Xall: killXserver
X
XkillXserver: killXserver.o
X	cc $(CFLAGS) -o killXserver killXserver.o
X
Xinstall: killXserver
X	mv killXserver $(INSBIN)
X	strip $(INSBIN)
X	chmod 4755 $(INSBIN)
X	cp $(MANPG) $(INSMAN)
X	chmod 0755 $(INSMAN)
X
Xshar:
X	shar README Makefile killXserver.c $(MANPG) > killXserver.shar
X
Xclean:
X	rm -f *.o
*EOF
if [ `wc -c <Makefile` -ne      548 ]
then
	echo "lengths do not match -- Bad Copy of Makefile"
fi
echo "extracting file killXserver.c"
sed -e 's/^X//' <<\*EOF > killXserver.c
X/* killXserver.c -- gives a user the facility to kill his own server. */
X/* (C) 1990 Blair P. Houghton, All Rights Reserved.  Copy and distribute *
X * this program freely as long as this copyright notice remains visible. */
X
X/* MUST BE INSTALLED SUID-ROOT. */
X
X#include <stdio.h>
X#include <signal.h>
X
X#define Notttyv(a,b) \
X  ( a[0] != 't' || a[1] != 't' || a[2] != 'y' || a[3] != 'v' || a[4] != b )
X
X
Xmain()
X{
X    int pid;
X    char display; /* tty id number; '0' or '1', since	         *
X	           * the only tty's allowed are ttyv0 and ttyv1. */
X    int serverpid();
X    char authorize();
X    void exit();
X    void perror();
X
X    /* Check console ownership; exits on failure. */
X    display = authorize();
X
X    /* Get this display's X-server's pid; exits on failure. */
X    pid = serverpid(display);
X
X    /* Reaching this point implies authority and intelligence. */
X    /* Do the deed. */
X    if ( kill(pid,SIGKILL) != 0 ) {
X	perror("kill");
X	fputs("server may not have been restarted, inform System Manager.\n",
X		stderr );
X	exit(1);
X    }
X}
X
X
Xint serverpid(d) /* Returns X-server's pid, exits on failure. */
Xchar d;		 /* Last char of tty name. */
X{
X    char line[BUFSIZ];
X    FILE *fp;
X    int pid;
X    void exit();
X
X    /* popen() a ps -aux.  This is much easier than	*
X     * implementing a kmem-peeker, but a lot slower...	*/
X    if ( (fp = popen("/bin/ps aux","r")) == (FILE *)NULL ) {
X	fputs("couldn't execute \"ps aux\", inform System Manager.\n",stderr);
X	exit(1);
X    }
X
X    while ( fgets(line,sizeof(line),fp) != (char *)NULL ) {
X
X	/* Check line for proper tty and process-name. */
X	if ( righttty(line,d) && server_entry(line) ) {
X
X	    pid = atoi(line+9);		/* Get the pid. */
X
X	    /* Fail-safe. */
X	    if ( pid == 0 || pid == 1 ) {
X		fprintf(stderr,
X			"tried to kill %s, inform System Manager.\n",
X			( pid == 0 ? "swapper" : "init process" ) );
X		exit(1);
X	    }
X
X	    pclose(fp);
X	    return(pid);		/* Successful retrieval. */
X	}
X
X    }
X
X    /* Reaching this point implies no server found. */
X    fputs("no X-server running, inform System Manager.\n",stderr);
X    exit(1);
X    /*NOTREACHED*/
X}
X
X
Xint righttty(s,t)
Xchar *s;
Xchar t;
X{
X    s += 49;  /* Skip to first possible char in command entry. */
X
X    /* Uses specialized string-contrasting macro. */
X    while ( Notttyv(s,t) ) {
X	if ( *(4 + ++s) == '\0' )	/* Check for EOL before looping. */
X	    return( 0 );		/* EOL */
X    }
X
X    return( 1 );		/* Must have matched before EOL */
X}
X
X
Xchar authorize() /* Checks whether user owns tty of *
X		 * current display to kill server. */
X{
X    char d;	/* Display number. */
X    char checktty();
X    char parsedisplay();
X    void exit();
X
X    /* Verify that program is run with euid == root. */
X    if ( geteuid() != 0 ) {
X	fputs(
X"must be superuser or run setuid-root killXserver, inform System Manager.\n",
X	    stderr);
X	exit(1);
X    }
X
X    d = parsedisplay();
X
X    /* Check tty ownership iff user is not really root. */
X    if ( getuid() != 0 )
X	checktty(d);	/* Exits on failure. */
X
X    return( d );	/* Display number is console number. */
X}
X
X
Xchar checktty(dispnum)
Xchar dispnum;
X{
X    FILE *fp;
X    char line[BUFSIZ];
X    char device[11];
X    char lsdevice[17];
X    char *userid;
X    void exit();
X    char *strcpy();
X    char *strcat();
X    FILE *popen();
X    int pclose();
X
X    strcpy(device,"/dev/ttyv_");
X    device[9] = dispnum;
X    strcpy(lsdevice,"ls -l ");
X    strcat(lsdevice,device);
X
X    /* Damn hack.  Couldn't stat(2) a character device... */
X    fp = popen( lsdevice, "r" );
X    fgets(line,sizeof(line),fp);
X    pclose(fp);
X
X    userid=cuserid((char *)NULL);	/* Appease the evaluation-order gods. */
X    if ( strncmp(userid, line+14, strlen(userid)) == 0 ) {
X		      /* line+14: ptr to name field of `ls -l' output. */
X
X	return;		/* Display's console is owned by user. */
X
X    }
X
X    /* Error by default.   Circumvent if root by     *
X     * checking for uid 0 before calling checktty(). */
X    fprintf(stderr,
X"you must be logged in on the console (%s) to restart the server.\n",
X	    device );
X    exit(1);
X}
X
X
Xchar parsedisplay()
X{
X    char host[32];
X    char *dis;
X    char *getenv();
X    void perror();
X    void exit();
X
X    if ( gethostname(host,32) != 0 ) {
X	perror("gethostname(2)");
X	fputs("couldn't determine host name, inform System Manager.\n",stderr);
X	exit(1);
X    }
X
X    if ( (dis = getenv("DISPLAY")) == (char *)NULL ) {
X	fputs("DISPLAY not defined, server not restarted.\n",
X		stderr );
X	exit(1);
X    }
X
X    if ( strncmp(dis,host,strlen(host)) != 0 ) {
X	fprintf(stderr,
X"killXserver: DISPLAY (%s) is not on local host (%s)\n\
Xserver not restarted.\n",
X		dis,host);
X	exit(1);
X    }
X
X    return( dis[1+strlen(host)] );	/* '0' or '1', display number, *
X					 * from, e.g., "buvlsi6:0.0"   */
X}
X
X
X/* Checks a line from a 'ps aux' for the server name 'Xqdsg' or 'Xqvsm' */
Xint server_entry(s)
Xchar *s;
X{
X    s += 49;  /* Skip to first possible char in command entry. */
X
X    while ( *s != 'X' || *(s+1) != 'q' ) {
X	if ( *(s++) == '\0' )
X	    return( 0 );	/* EOL */
X    }
X
X    if ( strncmp(s,"Xqdsg",5) == 0 || strncmp(s,"Xqvsm",5) == 0 )
X	return( 1 );		/* Matched. */
X
X    return( 0 );		/* "Xq", but neither "Xqdsg" nor "Xqvsm" */
X}
*EOF
if [ `wc -c <killXserver.c` -ne     5180 ]
then
	echo "lengths do not match -- Bad Copy of killXserver.c"
fi
echo "extracting file killXserver.l"
sed -e 's/^X//' <<\*EOF > killXserver.l
X.TH killXserver local
X.SH NAME
XkillXserver - restart the X server when it grows too large.
X.SH SYNTAX
X.ft B
X.nf
XkillXserver
X.fi
X.SH DESCRIPTION
X.I killXserver,
Xwhen run with effective user ID 0 (either
Xby running a setuid-root binary image of
X.I killXserver,
Xor by logging in as the superuser),
Xsends the signal SIGKILL to the X-server associated
Xwith the display (determined from the DISPLAY environment
Xvariable) that the user (determined from the real user ID)
Xis logged into.  Killing the X-server forces the
X.MS init 8
Xprocess to restart it.  The new process is clear of appendigial
Xmemory, which grows with usage in the
XUltrix 3.0 and 3.1
X.I Xqdsg
Xand
X.I Xqvsm
XX-servers.
X.PP
XThe user must be logged into the console window of the
XX display specified in the user's DISPLAY variable, and
Xmust run
X.PN killXserver
Xon that display's host.
XHowever,
X.PN killXserver
Xcan be run from any tty, pty, or window on that host,
Xas long as the user owns the display's console.
XThe superuser is not restricted in this manner, and
Xcan kill the X-server when the console is owned by
Xanother user.
X.SH DIAGNOSTICS
XMany, copious, and informative.  Some indicate that the
Xuser should inform the System Manager, since the conditions
Xthat raise them should be repaired immediately.
X.SH SEE ALSO
XX(1), kill(1), signal(3)
X.SH AUTHOR
XBlair P\. Houghton,
XECS Department,
XBoston University College of Engineering
X(bph@buengc.bu.edu)
*EOF
if [ `wc -c <killXserver.l` -ne     1412 ]
then
	echo "lengths do not match -- Bad Copy of killXserver.l"
fi
echo "Done."