/* Routines to read and write the /etc/utmp file. */ /* */ #include #include #include #include #define TRUE 1 #define FALSE 0 #define FAILURE -1 #define NULL 0 #define MAXFILE 79 /* Maximum pathname length for "utmp" file */ #ifdef DEBUG #undef UTMP_FILE #define UTMP_FILE "utmp" #endif static int fd = FAILURE ; /* File descriptor for the utmp file. */ static char utmpfile[MAXFILE+1] = UTMP_FILE ; /* Name of the current * "utmp" like file. */ static char utmplock[MAXFILE+5] ; static long loc_utmp ; /* Where in "utmp" the current "ubuf" was * found. */ static struct utmp ubuf ; /* Copy of last entry read in. */ /* "getutent" gets the next entry in the utmp file. */ struct utmp *getutent() { extern int fd ; extern char utmpfile[] ; extern struct utmp ubuf ; extern long loc_utmp,lseek() ; extern int errno ; register char *u ; register int i ; /* If the "utmp" file is not open, attempt to open it for */ /* reading. If there is no file, attempt to create one. If */ /* both attempts fail, return NULL. If the file exists, but */ /* isn't readable and writeable, do not attempt to create. */ if (fd == FAILURE) { if ((fd = open(&utmpfile[0],2)) == FAILURE) { /* If the open failed because the file didn't exist, then try to */ /* create one. */ if (errno == ENOENT) { if (creat(&utmpfile[0],0644) == FAILURE) return(NULL) ; close(fd) ; if ((fd = open(&utmpfile[0],2)) == FAILURE) return(NULL) ; } /* If the open failed for permissions, try opening it only for */ /* reading. All "pututline()" later will fail the writes. */ else if (errno == EACCES) { if ((fd = open(&utmpfile[0],0)) == FAILURE) return(NULL) ; } else return(NULL) ; } } /* Try to read in the next entry from the utmp file. If the */ /* read fails, return NULL. */ if (read(fd,&ubuf,sizeof(ubuf)) != sizeof(ubuf)) { /* Make sure ubuf is zeroed. */ for (i=0,u= (char *)(&ubuf); i < sizeof(ubuf) ; i++) *u++ = '\0' ; loc_utmp = 0 ; return(NULL) ; } /* Save the location in the file where this entry was found. */ loc_utmp = lseek(fd,0L,1) - (long)(sizeof(struct utmp)) ; return(&ubuf) ; } /* "getutid" finds the specified entry in the utmp file. If */ /* it can't find it, it returns NULL. */ struct utmp *getutid(entry) register struct utmp *entry ; { extern struct utmp ubuf ; struct utmp *getutent() ; register short type ; /* Start looking for entry. Look in our current buffer before */ /* reading in new entries. */ do { /* If there is no entry in "ubuf", skip to the read. */ if (ubuf.ut_time != 0) { if (strncmp(ubuf.ut_name,entry->ut_name,8)==0) return(&ubuf) ; break ; } } while (getutent() != NULL) ; /* Return NULL since the proper entry wasn't found. */ return(NULL) ; } /* "getutline" searches the "utmp" file for a LOGIN_PROCESS or */ /* USER_PROCESS with the same "line" as the specified "entry". */ struct utmp *getutline(entry) register struct utmp *entry ; { extern struct utmp ubuf,*getutent() ; register struct utmp *cur ; /* Start by using the entry currently incore. This prevents */ /* doing reads that aren't necessary. */ cur = &ubuf ; do { /* If the current entry is the one we are interested in, return */ /* a pointer to it. */ if (strncmp(entry->ut_line,cur->ut_line,8) == 0) return(cur) ; } while ((cur = getutent()) != NULL) ; /* Since entry wasn't found, return NULL. */ return(NULL) ; } /* "pututline" writes the structure sent into the utmp file. */ /* If there is already an entry with the same id, then it is */ /* overwritten, otherwise a new entry is made at the end of the */ /* utmp file. */ struct utmp *pututline(entry) struct utmp *entry ; { register int i,type ; struct utmp *answer ; struct stat statbuf ; extern long time() ; extern struct utmp ubuf ; extern long loc_utmp,lseek() ; extern struct utmp *getutid() ; extern int fd,errno ; struct utmp tmpbuf ; /* Copy the user supplied entry into our temporary buffer to */ /* avoid the possibility that the user is actually passing us */ /* the address of "ubuf". */ tmpbuf = *entry ; /* Create a lock link file before attempting to modify the utmp */ /* file. Wait for up to sixty seconds, but then continue */ /* regardless of the lock file. If sixty seconds isn't enough time,*/ /* the original creator of the lock probably has died. */ sprintf(&utmplock[0],"%s.lck",&utmpfile[0]) ; while (link(&utmpfile[0],&utmplock[0]) == FAILURE) { if (errno == EEXIST) { if (stat(&utmpfile[0],&statbuf) != FAILURE) { if (time(0) - statbuf.st_ctime > 60) unlink(&utmplock[0]) ; else sleep(3) ; } } else if (errno == ENOENT) { /* If the utmp file doesn't exist, make one by trying to find the */ /* entry of interest. "getutent()" will create the file. */ getutent() ; if (fd == FAILURE) { #ifdef ERRDEBUG gdebug("pututline: Unable to create utmp file.\n") ; #endif return((struct utmp *)NULL) ; } } else { #ifdef ERRDEBUG gdebug("pututline failed: link of lock file failed-%d",errno) ; #endif return((struct utmp *)NULL) ; } } /* Find the proper entry in the utmp file. Start at the current */ /* location. If it isn't found from here to the end of the */ /* file, then reset to the beginning of the file and try again. */ /* If it still isn't found, then write a new entry at the end of */ /* the file. (Making sure the location is an integral number of */ /* utmp structures into the file incase the file is scribbled.) */ if (getutid(&tmpbuf) == NULL) { #ifdef ERRDEBUG gdebug("First getutid() failed. fd: %d",fd) ; #endif setutent() ; if (getutid(&tmpbuf) == NULL) { loc_utmp = lseek(fd,0L,1) ; loc_utmp -= loc_utmp % sizeof(struct utmp) ; #ifdef ERRDEBUG gdebug("Second getutid() failed. fd: %d loc_utmp: %ld\n", fd,loc_utmp) ; #endif } } /* Seek to the proper place on the file descriptor for writing. */ lseek(fd,loc_utmp,0) ; /* Write out the user supplied structure. If the write fails, */ /* then the user probably doesn't have permission to write the */ /* utmp file. */ if (write(fd,&tmpbuf,sizeof(tmpbuf)) != sizeof(tmpbuf)) { #ifdef ERRDEBUG gdebug("pututline failed: write-%d\n",errno) ; #endif answer = (struct utmp *)NULL ; } else { /* Copy the user structure into ubuf so that it will be up to */ /* date in the future. */ ubuf = tmpbuf ; answer = &ubuf ; #ifdef ERRDEBUG gdebug("id: %c%c loc: %x\n",ubuf.ut_id[0],ubuf.ut_id[1], ubuf.ut_id[2],ubuf.ut_id[3],loc_utmp) ; #endif } /* Remove the lock file (even if it wasn't your own creation). */ unlink(&utmplock[0]) ; return(answer) ; } /* "setutent" just resets the utmp file back to the beginning. */ setutent() { register char *ptr ; register int i ; extern int fd ; extern struct utmp ubuf ; extern long loc_utmp ; if (fd != FAILURE) lseek(fd,0L,0) ; /* Zero the stored copy of the last entry read, since we are */ /* resetting to the beginning of the file. */ for (i=0,ptr=(char*)&ubuf; i < sizeof(ubuf) ;i++) *ptr++ = '\0' ; loc_utmp = 0L ; } /* "endutent" closes the utmp file. */ endutent() { extern int fd ; extern long loc_utmp ; extern struct utmp ubuf ; register char *ptr ; register int i ; if (fd != FAILURE) close(fd) ; fd = FAILURE ; loc_utmp = 0 ; for (i=0,ptr= (char *)(&ubuf); i < sizeof(ubuf) ;i++) *ptr++ = '\0' ; } /* "utmpname" allows the user to read a file other than the */ /* normal "utmp" file. */ utmpname(newfile) char *newfile ; { extern char utmpfile[] ; /* Determine if the new filename will fit. If not, return FALSE. */ if (strlen(newfile) > MAXFILE) return (FALSE) ; /* Otherwise copy in the new file name. */ else strcpy(&utmpfile[0],newfile) ; /* Make sure everything is reset to the beginning state. */ endutent() ; return(TRUE) ; } #ifdef ERRDEBUG #include gdebug(format,arg1,arg2,arg3,arg4,arg5,arg6) char *format ; int arg1,arg2,arg3,arg4,arg5,arg6 ; { register FILE *fp ; register int errnum ; extern int errno ; if ((fp = fopen("/etc/dbg.getut","a+")) == NULL) return ; fprintf(fp,format,arg1,arg2,arg3,arg4,arg5,arg6) ; fclose(fp) ; } #endif