[net.sources] Changing sysname and nodename dynamically.

jc@cdx39.UUCP (John Chambers) (11/07/86)

Enclosed is a fun little Un*x utility that allows you
to change your system's sysname, nodename, version,
etc. on the fly, without doing a system build.

The thing that is the most fun about this program is
that it shows how you might go about performing surgery
on a live system.  As coded, its default filename is
"/unix", so its effects will only be noticed after the
next boot.  But if you tell it to use "/dev/kmem", it
will change the name of the live system.  

This is a nice example of moving a "system hook" out
of the kernel and into user space, thus making the
kernel smaller and simpler.  (The concept does tend
to horrify many people, good system design though it
may be. :-)

Needless to say, you must be root to run this program.
However, to ease debugging, it will pretend to do its
job even if it is denied access.  

First, unpack it with 'sh'.  Then type:
	make setuname
When it compiles without error, type:
	setuname -d2 -sfoo -nbar -v8.3
This will set the debug level to 2, to give you an idea
of how it does its job.  It then sets the sysname to 'foo',
the nodename to 'bar', and the version to '8.3'.  It will
tell you that it is trying to modify "/unix", and will
tell you where the utsname structure was found.  It will
display a hex dump of the old and new structures.  It will
then fail to write the new value into /unix, because you
aren't root (I hope).

When you've satisfied yourself that it's not a Trojan 
Horse, you can su and change your system's name to what
you'd like it to be.

Note that all the documentation is within the source.  
There's no man page.  This is more or less intentional;
most people won't want this program to be easily visible
to all their users.

Enjoy.


: This is a shar archive. Extract with sh, not csh
echo file: Makefile
cat > Makefile << '\!Funky\!Stuff\!'
#-------------------------------------------------------------------#
# Program to change system and node names.
#
#
F=	-DSYS5 -I.
C=	cc $F -c 
L=	cc $F -o 

/usr/local/setuname: setuname ;	cp setuname /usr/local/setuname

setuname.L: setuname.c;		lint $F setuname.c dbg.c >setuname.L
setuname.s: setuname.c;		$C setuname.c
setuname  : setuname.c dbg.o;	$L setuname setuname.c dbg.o
#
dbg.o:    dbg.c dbg.h;		$C dbg.c
\!Funky\!Stuff\!
echo file: dbg.h
cat > dbg.h << '\!Funky\!Stuff\!'
#ifndef dbg_h
#include <stdio.h>
#include <sys/types.h>
/*
** Assorted types used by JC's debugging package.
*/
typedef unsigned char  Flag;
typedef unsigned char  U8;
typedef unsigned short U16;
typedef unsigned long  U32;

#define Done goto done
#define Fail goto fail
#define Loop goto loop
/*
** Debug levels; some advised conventions are:
**	debug=0	fatal error messages only.
**	debug=1	diagnostic messages and warnings, default.
**	debug=2	gabby; major events and decisions.
**	debug=3	tracing of likely trouble spots.
**	debug=4	tracing of important data values.
**	debug=5	major function calls and returns.
**	debug=6	details of flow of control.
**	debug=7	major data tracing.
**	debug=8	detailed data tracing.
**	debug=9	everything of conceivable interest.
*/
#define D  if(debug> 0)dmsg
#define D1 if(debug>=1)dmsg
#define D2 if(debug>=2)dmsg
#define D3 if(debug>=3)dmsg
#define D4 if(debug>=4)dmsg
#define D5 if(debug>=5)dmsg
#define D6 if(debug>=6)dmsg
#define D7 if(debug>=7)dmsg
#define D8 if(debug>=8)dmsg
#define D9 if(debug>=9)dmsg
/*
** Here are the primary dump macros, with a built-in debug-level arg.
*/
#define Dmpno(l,a,n,o)	if(debug>=(l))dbgdnom((char*)(a),(int)(n),(U32)(o),(char*)0)
#define Dmpnm(l,a,n,m)	if(debug>=(l))dbgdnom((char*)(a),(int)(n),(char*)0,(char*)(m))
#define Dmppo(l,a,p,o)	if(debug>=(l))dbgdpom((char*)(a),(char*)(p),(U32)(o),(char*)0)
#define Dmppm(l,a,p,m)	if(debug>=(l))dbgdpom((char*)(a),(char*)(p),(char*)0,(char*)(m))
/*
** For those who want an unconditional ASCII dump, here are the macros:
*/
#define Ascdump(a,n,o,m) ascdump((char*)(a),(U32)(n),(U32)(o),(char*)(m))
#define Ascd(p,q) 	Ascdump(p,q-p+1,p,0) 
#define Ascdpm(p,q,m)	Ascdump(p,q-p+1,0,m)
#define Ascdnm(a,n,m)	Ascdump(a,n,0,m)
/*
** For those who want an unconditional hex dump, here are the macros:
*/
#define Hexdump(a,n,o,m) hexdump((char*)(a),(U32)(n),(U32)(o),(char*)(m))
#define Hexd(p,q) 	Hexdump(p,q-p+1,p,0) 
#define Hexdpm(p,q,m)	Hexdump(p,q-p+1,0,m)
#define Hexdnm(a,n,m)	Hexdump(a,n,0,m)
/*
** The following names are obsolescent:
*/
#define Dmpnoa(l,a,n,o)	if(debug>=(l))Ascdump(a,n,o,0)
#define Dmpnma(l,a,n,m)	if(debug>=(l))Ascdump(a,n,0,m)
#define Dmppoa(l,a,p,o)	if(debug>=(l))Ascdump(a,p-a+1,o,0)
#define Dmppma(l,a,p,m)	if(debug>=(l))Ascdump(a,p-a+1,0,m)
#define Dmpnoh(l,a,n,o)	if(debug>=(l))Hexdump(a,n,o,0)
#define Dmpnmh(l,a,n,m)	if(debug>=(l))Hexdump(a,n,0,m)
#define Dmppoh(l,a,p,o)	if(debug>=(l))Hexdump(a,p-a+1,o,0)
#define Dmppmh(l,a,p,m)	if(debug>=(l))Hexdump(a,p-a+1,0,m)
/*
** Some 1-letter names for useful output functions:
*/
#define E emsg			/* Error message */
#define P pmsg			/* Unconditional print */
#define V if(debug>0)pmsg	/* Verbose, prints if any debug level */
/*
** Globals:
*/
extern int   ascdump();	/* Primary ASCII dump routine */
extern char *crlf;	/* Line terminator string for debug messages */
extern int   debug;	/* Global debug threshold */
extern Flag  dbghexfl;	/* If true, dumps are hex; if false, they are ASCII */
extern int   dbglevel;	/* How deep we are in function calls */
extern int   dbgsleep;	/* Time to sleep after each debug message, usually 0 */
extern FILE *dbgout;	/* Debug output file, usually stderr */
extern int   dsp();	/* Printable form of a char */
extern char *dbgtimep;	/* If non-null, timestamp for debug messages */
extern int   errno;	/* Unix error code */
extern int   hexdump();	/* Primary hexdump routine */
extern int   hex_dig();	/* Convert 0-15 to hex digit */
extern int   hex_word;	/* Number of bytes to group together in hex dumps */
extern char *progname;	/* Usually set to argv[0] */

#define dbg_h
#endif
\!Funky\!Stuff\!
echo file: dbg.c
cat > dbg.c << '\!Funky\!Stuff\!'
/* Debugging package for C programs.  The primary functions are
** based on conditional calls of printf(), with the program's
** name inserted at the start, a newline appended, and fflush()
** called to send it on its way.  There is also a hexdump()
** routine.  All these are intended to be called via the macros
** defined in "dbg.h".
**
** The global variable "debug" should be set to a value in [0,9],
** to control the amount of debugging output.
**
** Note that "dbgout" is a FILE*, either stdout or stderr as you
** wish, with stderr the default.
*/
#include "dbg.h"

#define MAXCOLS 132			/* Max length of print line */
#define MAXCHRS 100			/* Max chars in one hexdump chunk */

extern char *ctime();		/* Unix time conversion library routine */
extern int   errno;		/* Unix error code */
char  *crlf   = "\n";		/* Line terminator string, might be "\n\r" */
long   currtime;		/* Timestamp */
Flag   dbghexfl = 0;		/* If true, dumps default to hex */
int    dbglevel = 0;		/* Indent output by this much */
FILE  *dbgout = stderr;		/* Can be assigned to switch to stdout */
int    dbgsleep = 0;		/* Sleep time (secs) after messages */
int    debug  = 1;		/* Set to desired debug level (0-9) */
char  *dtimep  = 0;		/* Set to ASCII date/time if desired in output */
int    hex_chrs = MAXCHRS;	/* Number of chars to dump in one line */
int    hex_cols = MAXCOLS;	/* Length of print line */
int    hex_word = MAXCHRS;	/* Number of bytes to group together */
char  *progname = "?";		/* Set to argv[0] */
/*
** Debug output routine.  The program's name is
** prepended to the message, a terminator is added,
** and an optional sleep is done.
*/
/*VARARGS1*//*ARGSUSED*/
dmsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9) char *fmt;
{	int i, n;

	if (dbgout == NULL) return 0;
	n  = fprintf(dbgout,"%s: ",progname);
	for (i=1; i<=dbglevel; i++)
	     n += fprintf(dbgout,"+");
	n += fprintf(dbgout,fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	n += fprintf(dbgout,crlf);
	fflush(dbgout);
	if (dbgsleep) sleep(dbgsleep);
	return n;
}
/* Note that emsg() writes to both dbgout and stderr.
*/
/*VARARGS1*//*ARGSUSED*/
emsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9) char *fmt;
{	int n;

	if (dbgout == NULL) return 0;
	if (dbgout != stderr)
		n = dmsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	n += fprintf(stderr,"%s: ",progname);
	n += fprintf(stderr,fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	n += fprintf(stderr,crlf);
	fflush(stderr);
	return n;
}
/* Print a message to the debug output, adding only
** a terminator, but don't add program identification.
*/
/*VARARGS1*//*ARGSUSED*/
pmsg(fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9) char *fmt;
{	int n;

	if (dbgout == NULL) return 0;
	n  = fprintf(dbgout,fmt,x0,x1,x2,x3,x4,x5,x6,x7,x8,x9);
	n += fprintf(dbgout,crlf);
	fflush(dbgout);
	if (dbgsleep) sleep(dbgsleep);
	return n;
}
/* 
** Hexdump routine.  The params are a starting address,
** a byte count, an address to show on the output (which 
** is often the starting address), and an optional message
** for labeling the dump.  If the message is present, the
** address is not displayed, since they both would appear
** at the same place in the output.
**
** Note that the actual output is done by pmsg().
*/
static char  hex_cc[1+MAXCOLS];
static char  hex_hi[1+MAXCOLS];
static char  hex_lo[1+MAXCOLS];
static int   ascfl = 0;			/* True if hex dump not wanted */
/*
** Produce only the ASCII line; omit the two hex lines.
*/
ascdump(a,n,o,m)
	char *a;	/* Address of first byte to dump */
	U32   n;	/* Number of bytes */
	char *o;	/* Offset (address) to report */
	char *m;	/* Initial message, if o==NUL */
{	int   c;

	D9("ascdump(a=%06lX,n=%d,o=%06lX,m=%06lX)",a,n,o,m);
	if (dbgout == NULL) return 0;
	if (dtimep) fprintf(dbgout,"%s ",dtimep);
	if (m) sprintf(hex_cc,"%s            ",m);
	  else sprintf(hex_cc,"%7d           ",o);
	fprintf(dbgout,"%7.7s",hex_cc);
	fflush(dbgout);
	while (n-- > 0) {
	  c = *a++ & 0x7F;
	  switch (c) {
	  case '\0': fprintf(dbgout,"\\0"); break;
	  case '^' : fprintf(dbgout,"\\^"); break;
	  case '\\': fprintf(dbgout,"\\\\"); break;
	  case '\b': fprintf(dbgout,"\\b"); break;
	  case '\n': fprintf(dbgout,"\\n"); break;
	  case '\r': fprintf(dbgout,"\\r"); break;
	  case '\t': fprintf(dbgout,"\\t"); break;
	  case 0x7F: fprintf(dbgout,"\\D"); break;
	  default: 
	    if (c < ' ') {
	      fprintf(dbgout,"^%c",c|0x40);
	      break;
	    }
	    fprintf(dbgout,"%c",c);
	    break;
	  }
	}
	fprintf(dbgout,crlf);
	fflush(dbgout);
}
/*
*/
hexdump(a,n,o,m)
	char *a;	/* Address of first byte to dump */
	U32   n;	/* Number of bytes */
	U32   o;	/* Offset (address) to report */
	char *m;	/* Initial message, if o==NUL; -1 for ascii only */
{
	ascfl = 0;
	return hex_dump(a,n,o,m);
}
/*
*/
hex_dump(a,n,o,m)
	char *a;	/* Address of first byte to dump */
	U32   n;	/* Number of bytes */
	U32   o;	/* Offset (address) to report */
	char *m;	/* Initial message, if o==NUL; -1 for ascii only */
{	int   col, i;
	char  c;

	hex_init(m,o);
	col = 0;
	while (n) {	
		c = *a++;
		i = 7 + col + (col / hex_word);
		n--;
		hex_lo[i] = hex_dig(c & 0xF);
		hex_hi[i] = hex_dig((c >> 4) & 0xF);
		hex_cc[i] = dsp(c);
		col++;
		o++;
		if (col >= hex_chrs) {	
			hex_out();
			hex_init(m,o);
			col = 0;
		}
	}
	if (col > 0) hex_out();
	return 0;
}
/* Generate a printable char for x.  If x is an ASCII
** printable char, it is returned.  For others, '_' is
** returned.
*/
dsp(x) 
{	x &= 0x7F;
	return (0x20<=x && x<=0x7E) ? x : '_';
}
/* Given a small integer [0-15], this routine returns
** the corresponding ASCII char for a hex digit.
*/
hex_dig(x) 
{	return  ( 0<=x && x<= 9) ? '0' + x 
	: (10<=x && x<=15) ? 'A' + x - 10 
	: '_';
}
/* Initialize the line images for a given address.
*/
hex_init(m,a)
	char *m;	/* Initial message (<=8 chars) */
	U32   a;	/* Address reported on output */
{	int   i;

	i = hex_chrs + (hex_chrs / hex_word) + 8;	/* Length of print line (including final newline) */
	if (hex_cols < i) {
		hex_cols = i;
		D9("hex_init: cols=%d chrs=%d word=%d",hex_cols,hex_chrs,hex_word);
	}
	for (i=0; i<=hex_cols; i++)
		hex_cc[i] = hex_hi[i] = hex_lo[i] = ' ';
	if (m && a==0) {
		strcpy(hex_cc,m);
	} else {
		sprintf(hex_cc,"%6ld: ",a);
		sprintf(hex_hi,"%6lX: ",a);
	}
	return 0;
}
/* The three lines of hex dump info are complete;
** do a bit of straightening out, and print them.
*/
hex_out()
{	int i;

	if (dbgout == NULL) return 0;
	for (i=0; i<=hex_cols; i++) {		/* sprintf() may produce nulls */
		if (hex_cc[i] == 0) hex_cc[i] = ' ';
		if (hex_hi[i] == 0) hex_hi[i] = ' ';
		if (hex_lo[i] == 0) hex_lo[i] = ' ';
	}
	for (i=hex_cols; i>0; i--) 			/* Trim lines */
		if (hex_cc[i] == ' ') 
			hex_cc[i] = 0;
		else break;
	for (i=hex_cols; i>0; i--) 
		if (hex_hi[i] == ' ') 
			hex_hi[i] = 0;
		else break;
	for (i=hex_cols; i>0; i--) 
		if (hex_lo[i] == ' ') 
			hex_lo[i] = 0;
		else break;
	if (dtimep) fprintf(dbgout,"%s ",dtimep);
	pmsg("%s",hex_cc);			/* Symbolic dump */
	if (ascfl == 0) {
	    if (dtimep) fprintf(dbgout,"%s ",dtimep);
		pmsg("%s",hex_hi);		/* High-order hex digit */
	    if (dtimep) fprintf(dbgout,"%s ",dtimep);
		pmsg("%s",hex_lo);		/* Low-order hex digit */
	}
	if (dbgsleep) sleep(dbgsleep);
	return 0;
}
/* Dump an n-byte block starting at *s, 
** labelled either with a message or an offset.
*/
dbgdnom(a,n,o,m) char *a, *m; long o;
{	
	if (dbgout == NULL) return 0;
	if (debug <= 5) Ascdump(a,n,o,m); 
	if (debug >= 6) Hexdump(a,n,o,m); 
}
/* Dump a block starting at s, ending at t,
** labelled either with a message or an offset.
*/
dbgdpom(a,p,o,m) char *a, *p, *m; long o;
{
	if (dbgout == NULL) return 0;
	if (dbghexfl) Hexdump(a,p-a+1,o,m); 
		 else Ascdump(a,p-a+1,o,m); 
}
/*
** Get the current local time, return pointer to the ASCII version.
*/
char *getime()
{
	time(&currtime);		/* 32-bit UNIX timestamp */
	dtimep = ctime(&currtime);	/* Convert to ASCII */
	dtimep[24] = '\0';		/* We don't want the newline */
	return dtimep;
}
\!Funky\!Stuff\!
echo file: setuname.c
cat > setuname.c << '\!Funky\!Stuff\!'
/*    setuname [-option]... [file]
 *
 * Scan the file (which defaults to "/unix") for the current
 * utsname structure, and replace selected fields.  The options
 * understood are:
 *
 * -s<sysname>	changes the sysname field.
 * -n<nodename>	changes the nodename field.
 * -r<release>	changes the release field.
 * -v<version>	changes the version field.
 *
 * Some other options are:
 *
 * -d<n>	Debug level <n>; -d1 shows before-and-after values.
 * -f<f>	Modify file <f>.
 * -h		Write a "help" message containing this information.
 *
 * If the debug flag is set, the program will ignore failure to get
 * write permission, and will attempt to make the changes (which will
 * fail, but you'll see what it would have done).
 *
 * Normally this is done to /dev/kmem to change the fields in the live
 * system, or in /unix to change then at the next boot.  You have to be
 * super-user, of course, to zap the kernel in this way.
 *
 * Note that this program must be linked to the dbg module to work.
 */
#include "dbg.h"
#include <errno.h>
#include <sys/utsname.h>

#define N 512

long   adr0 = 0;		/* Address used in lseeks */
long   adr1 = 0x7FFFFFFF;
char   ibuf[1+N];		/* Input buffer */
char  *bp = ibuf+N;
char  *bq = ibuf+N;
char  *bz = ibuf+N;
char  *filedfl   = "/unix";	/* Default filename */
char  *filename  = 0;		/* Desired filename */
int    filen  = -1;		/* File number */
long   limit  = 1;		/* Number of occurrences to report */
char  *nodename  = 0;		/* Desired nodename */
long   offset = 0;		/* Current position within file */
char  *release  = 0;		/* Desired release */
int    sn = 0;			/* Size of data structure */
char  *sp = NULL;		/* Scratch pointer */
char  *sysname  = 0;		/* Desired sysname */
struct utsname  utsname = {0};	/* Current system-name structure */
struct utsname *up = 0;		/* Pointer to utsname structure in buffer */
char  *version  = 0;		/* Desired version */

main(ac,av)
	char **av;
{	int   a, i, n;
	char  c, *p, *q;
	long  l;

	progname = av[0];		/* For debug module's messages */
	for (a=1; a<ac; a++) {	/* Handle command-line options */
		D4("arg %d=\"%s\"",a,av[a]);
		c = av[a][0];
		if (c == '-' || c == '+') {
			switch(av[a][1]) {
			default:	E("Can't handle \"%s\"",av[a]);
					break;
			case 'h': case 'H': help(); break;
			case 'd': case 'D':
					i = sscanf(av[a]+2,"%d",&debug);
					if (i < 1) debug = 1;
					D3("debug=%d",debug);
					break;
			case 'f': case 'F': 
					filename = av[a] + 2;
					D2("filename=\"%s\"",filename);
					break;
			case 'n': case 'N': 
					nodename = av[a] + 2;
					D2("nodename=\"%s\"",nodename);
					break;
			case 'r': case 'R': 
					release = av[a] + 2;
					D2("release=\"%s\"",release);
					break;
			case 's': case 'S': 
					sysname = av[a] + 2;
					D2("sysname=\"%s\"",sysname);
					break;
			case 'v': case 'V': 
					version = av[a] + 2;
					D2("version=\"%s\"",version);
					break;
			}
		} else {		/* Not an option */
			if (filename == 0) {
				filename = av[a];
				D2("filename=\"%s\"",filename);
			} else {
				E("Arg \"%s\" ignored.",sp);
			}
		}
	} /*end for*/
	if (filename == 0)
			filename = filedfl;
	if ((filen = open(filename,2)) < 0) {
		if (debug) {			/* If debugging, */
			E("Can't write \"%s\" [going ahead anyway, for debugging]",filename);
			filen = open(filename,0);		/* try it read-only */
		}
	}
	if (filen < 0) {
		E("Can't open \"%s\"",filename);
		Fail;
	}
	n = uname(&utsname);
	if ( release == 0)  release = utsname. release;
	if ( sysname == 0)  sysname = utsname. sysname;
	if (nodename == 0) nodename = utsname.nodename;
	if ( version == 0)  version = utsname. version;
	D1("    filename=\"%s\"",       filename);
	D1("Old  sysname=\"%s\"",utsname.sysname);
	D1("Old nodename=\"%s\"",utsname.nodename);
	D1("Old  release=\"%s\"",utsname.release);
	D1("Old  version=\"%s\"",utsname.version);
	D1("New  sysname=\"%s\"",        sysname);
	D1("New nodename=\"%s\"",       nodename);
	D1("New  release=\"%s\"",        release);
	D1("New  version=\"%s\"",        version);
	sp = (char*)&utsname;			/* This is what we're looking for */
	sn = sizeof(utsname.sysname) 		/* This is how big it is */
	   + sizeof(utsname.nodename)
	   + sizeof(utsname.release)
	   + sizeof(utsname.version);
	D4("%d-byte structure",sn);
	D4("adr0=%08lx=%ld",adr0,adr0);
	if (adr0 > 0) {			/* Nonzero starting address? */
		offset = lseek(filen,adr0,0);	/* Find the sp's address */
		if (offset < 0) {
			fprintf(stderr,"Can't seek to %04lx=%ld\n",adr0,adr0);
			Fail;
		}
	}
	while (offset < adr1) {		/* Once per offset byte */
		if ((bq-bp) < sn) {			/* New input needed? */
			D4("%d bytes left, read needed.",bq-bp);
			for (p=ibuf,q=bp; q<bq; )		/* Move tail to start of buffer */
				*p++ = *q++;
			bp = p;				/* Place to read next chunk into */
			*p = errno = 0;

			n = read(filen,bp,bz-bp);		/* Read another chunk */

			D3("read(%d,%06lX,%d)=%d\t[errno=%d]\toffset=%06lX=ld",filen,bp,bz-bp,n,errno,offset,offset);
			D4("%d-byte chunk read, offset=%06lX=%ld",n,offset,offset);
			if (debug >= 5) Hexdump(ibuf,n,offset,0);
			if (n < 0) {
				fprintf(stderr,"Error %d reading input; offset=%06lX=%ld.\n",errno,offset,offset);
				fprintf(stderr,"Attempted operation: read(%d,%06lX,%d)\n",filen,bp,bz-bp);
				Fail;
			}
			if (n == 0) {
				D4("Not found");
				Fail;
			}
			bq = bp + n;
			bp = ibuf;
			*bq = '\0';
			D4("bp=%04lx bq=%04lx",bp,bq);
		}
		D7("bp:\"%s\"",bp);
		D7("sp:\"%s\"",sp);
		if (strncmp(bp,sp,sn) == 0) {	/* Got it! */
			D1("Offset=%lX=%ld",offset,offset);
			if (--limit <= 0) {		/* Is offset acceptable? */
				for (p=bp; p<bp+sn; )
					*p++ = 0;
				up = (struct utsname *)bp;	/* To satisfy strict-typing rules */
			
				if (q = sysname) {		/* New sysname field? */
					p = up->sysname;
					while (p < up->sysname+9  && *q) *p++ = *q++;
					while (p < up->sysname+9)        *p++ = 0;
				}
				if (q = nodename) {		/* New nodename field? */
					p = up->nodename;
					while (p < up->nodename+9  && *q) *p++ = *q++;
					while (p < up->nodename+9)        *p++ = 0;
				}
				if (q = release) {		/* New release field? */
					p = up->release;
					while (p < up->release+9  && *q) *p++ = *q++;
					while (p < up->release+9)        *p++ = 0;
				}
				if (q = version) {		/* New version field? */
					p = up->version;
					while (p < up->version+9  && *q) *p++ = *q++;
					while (p < up->version+9)        *p++ = 0;
				}
				if (debug >= 2) {
					P("New utsname structure in \"%s\" at offset=%06lX=%ld:",filename,offset,offset);
					Hexdnm(bp,sn," ");
				}
				errno = 0;
				D3("before:lseek(%d,%06lX,%d)",filen,offset,0);
				l = lseek(filen,offset,0);
				D3("after: lseek(%d,%06lX,%d)=%ld\t[errno=%d]",filen,offset,0,l,errno);
				errno = 0;
				D3("before:write(%d,%06lX,%d)",filen,bp,sn);
			
				i = write(filen,bp,sn);
			
				D3("after: write(%d,%06lX,%d)=%d\t[errno=%d]",filen,bp,sn,i,errno);
				if (debug)
					P("The new names will take effect after the next boot from \"%s\"",filename);
				Done;
			}
		}
		++bp;
		++offset;
	} /*end while*/
	D4("Past limit %04lx",adr1);
fail:
	E("Didn't find utsname structure in \"%s\"; no changes made.",filename);
	exit(1);
done:
	exit(0);
} /*end main()*/
help()
{
	P("Usage: %s [-options]... [+options]... <file",progname);
	P("Options:");
	P("\t-a<n>    Start at address <n> [disk files only].");
	P("\t+a<n>    Stop at address <n> [disk files only].");
	P("\t-d<n>    Debug level <n>.");
	P("\t+d<n>    Debug level <n>.");
	P("\t-f<f>    Modify file <f> [default=\"/unix\"].");
	P("\t-h       Help (show this output).");
	P("\t-n<s>    New nodename <s>.");
	P("\t-r<s>    New release <s>.");
	P("\t-s<s>    New sysname <s>.");
	P("\t-v<s>    New version <s>.");
}
\!Funky\!Stuff\!
-- 

	John M Chambers 
Phone: 617/364-2000x7304
Email: ...{adelie,bu-cs,harvax,inmet,mcsbos,mit-eddie,mot[bos],rclex}!cdx39!{jc,news,root,usenet,uucp}
Smail: Codex Corporation; Mailstop C1-30; 20 Cabot Blvd; Mansfield MA 02048-1193
Telex: 922-443 CODEX A MNSF
Clever-Saying: For job offers please call at home (617/484-6393) evenings and weekends.