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.