Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

fakeutmp - simple program to create a utmp entry

5 views
Skip to first unread message

Nelson Minar

unread,
Feb 14, 1992, 8:37:07 PM2/14/92
to
I frequently create UNIX shells with no entry in /etc/utmp, especially
under xterm or UW. This is usually nice, as it avoids cluttering up
/etc/utmp with all 15 of my shells, but it can be inconvenient when I
need to talk or write someone.

So I wrote this little program to enter a given tty in /etc/utmp. It
finds out what tty it's running on and creates an entry in /etc/utmp
on that tty, with appropriate error checking to make sure it doesn't
clobber anything that's already there. It then waits to receive a kill
signal, or for its parent to die, at which point it removes your utmp
entry and exits.

The program will have to be able to write on /etc/utmp - we've made
utmp writeable by group utmp and make fakeutmp setgid utmp. I've
successfully run it under Ultrix 4.2, but make no claims that it will
work anywhere else in the world.

----------------------------cut here----------------------------

/* fakeutmp by nel...@reed.edu. Fri Feb 14 17:14:51 PST 1992
* usage: fakeutmp [host]
* Creates an entry in /etc/utmp on your current tty, then monitors for
* the parent to exit or a variety of quit signals, at which point it
* cleans up after itself and exits.
* Useful when you have a shell without a utmp entry, and you need one.
* Note that this program must have write permission on /etc/utmp. I
* recommend putting /etc/utmp in some convenient group (group utmp, for
* example) and making fakeutmp setgid. You could also make /etc/utmp
* world writeable (very bad idea), or you can run fakeutmp setuid
* root. While I *think* this program is safe to use setuid root, I
* make no promises. Do the setgid thing.
*
* This program successfully runs under Ultrix 4.2, once you compile it
* (Ultrix cc doesn't support the string concatenation used in the
* perror() calls. I refuse to write code for broken compilers,
* so I use gcc.)
* I'd guess it will run on other UNIX machines, modulo finding the
* necessary include files (utmp.h, in particular)
*/

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <pwd.h>
#include <string.h>
#include <utmp.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>

#define UTMPFILE "/etc/utmp"
#define DEFAULT_HOST "" /* default host string to insert */

int verboseOutput = 0; /* we could make this an option, but */
/* I didn't want to bother */

/* info needs to be shared across two functions for signal handler */
int ttySlot;
char * ttyName;
struct utmp utmpEntry;
struct passwd userInfo;

void cleanupEntry();
void printUtmp(struct utmp *);

int main(int argc, char * argv[]) {
char ttyPath[256];
pid_t parentPID;
int rc;
int utmpFD;
time_t timeNow;

parentPID = getppid();
userInfo = *(getpwuid(getuid()));

/* figure out what tty we're really on. */
strcpy(ttyPath, ttyname(0));
ttyName = strrchr(ttyPath, '/');
if (ttyName == NULL) {
fprintf(stderr, "tty name %s invalid\n", ttyPath);
return 1;
}

/* figure out what tty slot we're on. */
ttySlot = ttyslot();
if (ttySlot == 0) {
fprintf(stderr, "Error accessing your tty slot\n");
exit(3);
}

if (verboseOutput)
printf("Creating entry for %s on %s (parent is %d)\n", userInfo.pw_name, ttyName+1, (int)parentPID);

/* open up the utmp file */
utmpFD = open(UTMPFILE, O_RDWR);
if (utmpFD < 0) {
perror("Couldn't open "UTMPFILE);
exit(2);
}

/* read in the utmp entry for our tty slot */
lseek(utmpFD, ttySlot * sizeof utmpEntry, L_SET);
rc = read(utmpFD, &utmpEntry, sizeof utmpEntry);
if (rc < 0) {
perror("Error while reading from /etc/utmp");
exit(1);
}

/* check out the utmp entry - make sure it's empty. */
if (rc > 0)
if (utmpEntry.ut_name[0] != (char)0) {
fprintf(stderr, "There is already an entry on your tty.\n");
printUtmp(&utmpEntry);
exit(4);
}

/* everything is OK, build utmp entry */
strncpy(utmpEntry.ut_name, userInfo.pw_name, 8);
strncpy(utmpEntry.ut_line, ttyName + 1, 8);
if (argc < 2)
strncpy(utmpEntry.ut_host, DEFAULT_HOST, 16);
else
strncpy(utmpEntry.ut_host, argv[1], 16);

time(&timeNow);
utmpEntry.ut_time = timeNow;

/* go ahead and write it in */
lseek(utmpFD, ttySlot * sizeof utmpEntry, L_SET);
rc = write(utmpFD, &utmpEntry, sizeof utmpEntry);

/* check to make sure it wrote. If not, try to clean up the mess */
if (rc != sizeof utmpEntry) {
fprintf(stderr, "There was an error while writing your utmp entry. Trying to clean up.\n");
bzero(&utmpEntry, sizeof utmpEntry);
write(utmpFD, &utmpEntry, sizeof utmpEntry);
exit(5);
}

/* if this were really intended to be setuid root, we could leave the FD
* open and do a setreuid() here. That way, a user could send the process
* signals. Instead we'll close the file off and be blissfully unaware if
* we're running setuid.
*/
close(utmpFD);

#ifdef DEBUG
getchar();
#else
/* set up signal handlers */
signal(SIGHUP, cleanupEntry);
signal(SIGINT, cleanupEntry);
signal(SIGQUIT, cleanupEntry);
signal(SIGTERM, cleanupEntry);

/* now lets idle, waiting for someone to die */
while(kill(parentPID, 0) == 0) /* as long as parent is alive */
sleep(10);
#endif

cleanupEntry(0, 0, (struct sigcontext *)0); /* this routine exits for us */
}

/* remove the old utmp entry if it's safe. */
void
cleanupEntry(int sig, int code, struct sigcontext *scp) {
int rc;
struct utmp tempUE;
int utmpFD;

/* open up the utmp file */
utmpFD = open(UTMPFILE, O_RDWR);
if (utmpFD < 0) {
perror("Couldn't open "UTMPFILE);
exit(10);
}

/* read in the utmp entry again. */
lseek(utmpFD, ttySlot * sizeof tempUE, L_SET);
rc = read(utmpFD, &tempUE, sizeof tempUE);
if (rc != sizeof tempUE) {
fprintf(stderr, "There was an error reading /etc/utmp. Your entry has not been erased\n");
exit(11);
}

/* make sure the utmp entry hasn't changed while we were away */
if ((strncmp(tempUE.ut_name, utmpEntry.ut_name, 8) != 0) ||
(strncmp(tempUE.ut_line, utmpEntry.ut_line, 8) != 0) ||
tempUE.ut_time != utmpEntry.ut_time) {
fprintf(stderr, "The utmp entry was changed out from under you. Not erasing it.\n");
if (verboseOutput)
printUtmp(&tempUE);
exit(12);
}

/* write out a null utmp entry */
bzero(&tempUE, sizeof tempUE);
lseek(utmpFD, ttySlot * sizeof tempUE, L_SET);
rc = write(utmpFD, &tempUE, sizeof tempUE);
if (rc != sizeof tempUE) {
perror("There was an error while erasing your utmp entry");
exit(13);
}

exit(0); /* all done! */
}

/* print out a utmp entry. Stupid utmp doesn't guarantee null returns */
void
printUtmp(struct utmp * u) {
char name[9], line[9], host[17];
time_t timeInUtmp;

strncpy(name, u->ut_name, 8);
name[8] = 0;
strncpy(line, u->ut_line, 8);
line[8] = 0;
strncpy(host, u->ut_host, 16);
host[16] = 0;
timeInUtmp = u->ut_time;

fprintf(stderr, "%s\t %s (%s) %s", name, line, host,
ctime(&timeInUtmp));
}

----------------------------cut here----------------------------
--
__
nel...@reed.edu \/ A fox passed me.. once

0 new messages