/* * $Id: users.c,v 1.5 2002/08/23 13:38:15 howardjp Exp $ * * Copyright (c) 1990 * Jan Wolter. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Jan Wolter * and his contributors. * 4. Neither the name of Jan Wolter nor the names of his contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY JAN WOLTER AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL JAN WOLTER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* PARTY PROGRAM -- USER LIST ROUTINES -- Jan Wolter */ #include "party.h" #include "opt.h" #include #define CMD_LEN 20 #define SUB_PARTY 8 /* PWHO FILE: this is a file basically similar to the utmp file, that keeps * track of who is on what party channel. It is a binary file with entries * for each tty line. To prevent strange contention problems, no party * process should ever write any line of the pwho file except the one * corresponding to it's own tty. * * Each pwho entry consists of one header, giving the ttyname and the * logname of the user on that line. This is followed by up to eight * subparty lines for party invocations that have not exitted. (Multiple * invocations can occur when users shell out or suspend their process.) * Each of these give that invocation's internal name, the time the process * started, the current channel, and any current shell command. * * One problem with this is that a hostile user can do a "kill -9" on his * party process, and leave without having his pwho entry erased, thus * fooling the pwho program into thinking he is still there. We try to * detect such chicanery by comparing the utmp file to the pwho file. If * the start time of the process preceeds the login time of the user on that * line, we can safely assume that entry is a dud. * * We indicated users who have suspended with a "^" in the shelled line. */ struct pwho { /* HEADER: null name means no user on line */ char line[UT_LINESIZE]; /* user's tty name, as in utmp file */ char logname[UT_NAMESIZE]; /* user's name, as in utmp file */ }; struct sub_pwho { /* SUBFIELD: null alias invalidates entry */ char alias[UT_NAMESIZE]; /* user's name inside party */ long time; /* time the user entered party */ char channel[CHN_LEN]; /* user's current channel number */ char shelled[CMD_LEN]; /* name of command user shelled to */ }; #define ENTRY_SIZE (sizeof(struct pwho) + SUB_PARTY * sizeof(struct sub_pwho)) /* ULIST STRUCTURE: The following structure used to store linked lists of users * to be output. It includes only the info to be displayed. */ struct ulist { char alias[UT_NAMESIZE]; long time; char channel[CHN_LEN]; char shelled[CMD_LEN]; struct ulist *next; }; FILE *pwfp= NULL; struct pwho myhead; struct sub_pwho mybody; long myoff, mysuboff; char *ttyname(); /* WHO_ENTER: Called when the user enters party. If finds a slot for him in * the partywho file and writes in his current status. */ int who_enter() { long off; int i; struct pwho ahead; struct sub_pwho abody; /* Setup our entry */ strncpy(myhead.line,logtty+5,UT_LINESIZE); strncpy(myhead.logname,logname,UT_NAMESIZE); mybody.time= time(0L); if (name) strncpy(mybody.alias,name,UT_NAMESIZE); if (channel) strncpy(mybody.channel,channel,CHN_LEN); strncpy(mybody.shelled,"",CMD_LEN); /* Scan for our entry */ for (off= 0L;;off += ENTRY_SIZE) { fseek(pwfp,off,0); if (fread(&ahead,sizeof(struct pwho),1,pwfp) == 0) { /* Create slot for us at end of file */ myoff = off; mysuboff = off + sizeof(struct pwho); break; } if (!strncmp(myhead.line,ahead.line,UT_LINESIZE)) { /* Found existing slot for us */ myoff = off; if (strncmp(myhead.logname,ahead.logname,UT_NAMESIZE) || fread(&abody,sizeof(struct sub_pwho),1,pwfp) == 0 || abody.alias[0] == '\0' || abody.time < logtime) { /* No valid user in it -- take first subslot */ who_clear(off,myhead.line); mysuboff = off + sizeof(struct pwho); } else { off += sizeof(struct pwho); /* We're already there -- use first free slot */ for (i= 1; i CMD_LEN) len= CMD_LEN; ncstrncpy(mybody.shelled,cmd,len); if (!pwfp || !mysuboff) return; fseek(pwfp,mysuboff,0); fwrite(&mybody,sizeof(struct sub_pwho),1,pwfp); fflush(pwfp); } /* WHO_SHIN: Record that the user has returned from a shell escape. */ who_shin() { strncpy(mybody.shelled,"",CMD_LEN); if (!pwfp || !mysuboff) return; fseek(pwfp,mysuboff,0); fwrite(&mybody,sizeof(struct sub_pwho),1,pwfp); fflush(pwfp); } /* WHO_ISOUT: Check if the users is currently shelled out */ int who_isout() { return (mybody.shelled[0] != '\0'); } /* WHO_LIST: Print a list of who is on. The sortkey may be * 'n' - sort by user name * 'c' - sort by conference * 't' - sort by time */ who_list(fp,sortkey) FILE *fp; char sortkey; { struct pwho ahead; struct sub_pwho abody; int i,n; struct ulist *head= NULL, *curr, *next; wscan_init(); /* Scan file and load into ulist */ while (wscan_next(&ahead,&abody,&n)) add_ulist(&head, abody.alias, abody.channel, abody.time, abody.shelled, sortkey); wscan_done(); /* Print Headings */ fprintf(fp,"User"); for (i= 4; i < UT_NAMESIZE; i++) putc(' ',fp); fprintf(fp," Started Channel\n"); /* Print ulist */ for (curr= head; curr != NULL; curr= curr->next) { fprintf(fp,"%-*.*s %15.15s %.*s", UT_NAMESIZE,UT_NAMESIZE,curr->alias, ctime(&curr->time)+4, CHN_LEN,curr->channel); if (curr->shelled[0] == '\0') fprintf(fp,"\n"); #ifdef BSD else if (curr->shelled[0] == '^' && curr->shelled[1] == '\0') fprintf(fp," (suspended)\n"); #endif /*BSD*/ else fprintf(fp," (shelled to %s)\n",curr->shelled); } /* Deallocate ulist */ for (curr= head, next= NULL; curr != NULL; curr= next) { next= curr->next; free(curr); } } /* ADD_ULIST: Add a user entry to the ulist, inserting it in the list in the * proper place as selected by sortkey. */ add_ulist(head,al,ch,tm,sh,sortkey) struct ulist **head; char *al,*ch,*sh; long tm; char sortkey; { struct ulist *new= (struct ulist *)malloc(sizeof(struct ulist)); struct ulist *curr, *prev; int c_ch, c_al, c_tm; new->time= tm; strncpy(new->alias,al,UT_NAMESIZE); strncpy(new->channel,ch,CHN_LEN); ncstrncpy(new->shelled,sh,CMD_LEN); for (curr= *head, prev= NULL; curr != NULL; prev= curr, curr=curr->next) { c_ch= strncmp(ch,curr->channel,CHN_LEN); c_al= strncmp(al,curr->alias,UT_NAMESIZE); c_tm= (tm < curr->time) ? -1 : 1; if ((sortkey == 'c' && (c_ch<0 || (c_ch==0 && (c_al < 0 || (c_al == 0 && c_tm < 0))))) || (sortkey == 'n' && (c_al<0 || (c_al==0 && c_tm < 0))) || (sortkey == 't' && c_tm < 0)) break; } /* Insert the new element before curr */ if (prev == NULL) *head= new; else prev->next= new; new->next= curr; } #ifndef NOCLOSE /* WHO_ISON: Print a list of who is on the named channel. This just prints * the real names of the users. It is mainly used for initializing .usr * files for closed channels. */ who_ison(fp,chn) FILE *fp; char *chn; { struct pwho ahead; struct sub_pwho abody; int n; wscan_init(); while (wscan_next(&ahead,&abody,&n)) { if (!strncmp(chn,abody.channel,CHN_LEN)) fprintf(fp,"%-.*s\n",UT_NAMESIZE,ahead.logname); } wscan_done(); } #endif /*NOCLOSE*/ /* WHO_CLEAR: Erase an entry in the pwho file */ who_clear(off,line) long off; char *line; { struct pwho ahead; struct sub_pwho abody; int i; if (!pwfp) return; fseek(pwfp,off,0); strncpy(ahead.line,line,UT_LINESIZE); strncpy(ahead.logname,"",UT_NAMESIZE); fwrite(&ahead,sizeof(struct pwho),1,pwfp); strncpy(abody.alias,"",UT_NAMESIZE); for(i=0;inext) { if (!strncmp(ch->name,abody.channel,CHN_LEN)) break; } if (ch) ch->users++; else head= addchn(head,abody.channel,1); } wscan_done(); return(head); } /* WHO_UNIQALIAS: Is the given alias used only by the given user in the * given channel? */ int who_uniqalias(alias,name,channel) char *alias, *name, *channel; { struct pwho ahead; struct sub_pwho abody; int n, skipping= 0; wscan_init(); while (wscan_next(&ahead,&abody,&n)) { /* If this belongs to me, skip the entire entry */ if (n == 1) skipping= !strncmp(name, ahead.logname, UT_NAMESIZE); if (skipping) continue; /* Ignore users in other channels */ if (strncmp(channel, abody.channel, CHN_LEN)) continue; if (!strncmp(alias, abody.alias, UT_NAMESIZE)) { wscan_done(); return(0); } } wscan_done(); return(1); } /********* P A R T Y T M P S C A N N I N G R O U T I N E S *********/ struct utmp *utmp= NULL; /* Pointer to internal copy of the utmp file */ int nutmp; /* Number of entries in utmp */ int subcnt; /* Number of subfields read so far */ long curtime; /* Time stamp from utmp file */ /* WSCAN_INIT -- This initializes for a new scan of the partytmp file. */ wscan_init() { FILE *utfp; struct stat utst; if (!pwfp) return; /* Open utmp file */ if ((utfp= fopen(UTMP,"r")) == NULL) { err("cannot open utmp file %s\n",UTMP); return; } if (utmp != NULL) free(utmp); /* Load a copy of utmp file into memory */ fstat(fileno(utfp),&utst); utmp= (struct utmp *)malloc((mtype)(utst.st_size+2*sizeof(struct utmp))); for (nutmp=0;fread(utmp+nutmp,sizeof(struct utmp),1,utfp);nutmp++) ; fclose(utfp); fseek(pwfp,0L,0); subcnt= 0; } /* WSCAN_NEXT -- Get the next valid head/body pair from the party file. If * there isn't one, return 0. 'n' gives the subfield number of the current * entry, 1 for the first subfield, and so on. wscan_init() must be called * shortly before the first call to this. wscan_done() should be called after * it is complete. */ int wscan_next(phead,pbody,n) struct pwho *phead; struct sub_pwho *pbody; int *n; { int j; int ignore; if (!pwfp) return(0); for (;;) { if (subcnt == 0) { /* Get the next header */ if (fread(phead,sizeof(struct pwho),1,pwfp) == 0) return(0); /* Look for matching, non-obsolete utmp line */ for(j= 0; jline,UT_LINESIZE) == 0 && !strncmp(utmp[j].ut_name,phead->logname,UT_NAMESIZE)) break; } /* If no utmp entry exists for this line or the user on the line * isn't the one in the partytmp file, then this is an obsolete * entry. We will ignore it. */ ignore= (j == nutmp); curtime= utmp[j].ut_time; } else ignore= 0; /* Read in sub-records */ for (subcnt++; subcnt<=SUB_PARTY; subcnt++) { if (fread(pbody,sizeof(struct sub_pwho),1,pwfp) == 0) return(0); else if (!ignore && pbody->alias[0] != '\0' && pbody->time >= curtime) { *n= subcnt; return(1); } } subcnt= 0; } } /* WSCAN_DONE -- Finish up a scan of the partytmp file */ int wscan_done() { free(utmp); utmp= NULL; } /* NCSTRNCPY -- Do a string copy, replacing non-printable characters with ? */ ncstrncpy(s1,s2,n) char *s1, *s2; int n; { for (;*s2 != '\0' && n > 0; s1++,s2++,n--) { if (isascii(*s2) && isprint(*s2)) *s1= *s2; else *s1= '?'; } for (;n > 0; s1++,n--) *s1= '\0'; } /* FINDUSER - Figure out the user's control tty and /etc/utmp name in a fairly * robust manner. This is generally tougher to fool than getlogin(). This * returns TRUE if the the user cannot be found for any reason. */ int finduser(logtty,logname,logtime) char *logtty, *logname; long *logtime; { FILE *fp; char *tty; struct utmp ut; int i; /* See if we can find a useful ttyname for stderr, stdout, or stdin */ for (i= 2; i >= 0; i--) { if ((tty= ttyname(i)) == NULL || strcmp(tty,"/dev/tty")) break; } if (i < 0) { err("cannot identify your tty\n" "Try running %s from a different prompt\n",progname); return(1); } strcpy(logtty,tty); /* Open the utmp file */ if ((fp= fopen(UTMP,"r")) == NULL) { err("Cannot open utmp file %s\n",UTMP); return(1); } /* Search the utmp file */ while (fread(&ut,sizeof(struct utmp),1,fp) != 0) { if (!strncmp(ut.ut_line,logtty+5,UT_LINESIZE)) { fclose(fp); *logtime= ut.ut_time; strncpy(logname,ut.ut_name,UT_NAMESIZE); logname[UT_NAMESIZE]= '\0'; return(0); } } err("Cannot find your tty (%s) in utmp\n",logtty); fclose(fp); return(1); }