/* Copyright (c) 1993 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de) * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de) * Copyright (c) 1987 Oliver Laumann * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING); if not, write to the * Free Software Foundation, Inc., * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA * **************************************************************** */ #include "rcs.h" RCS_ID("$Id: utmp.c,v 1.7 1994/05/31 12:33:21 mlschroe Exp $ FAU") /* * An explanation of some weird things: * * linux should have GETUTENT, but their pututline() doesn't have * a return value. * * UTNOKEEP: A (ugly) hack for apollo that does two things: * 1) Always close and reopen the utmp file descriptor. (I don't know * for what reason this is done...) * 2) Implement an unsorted utmp file much like GETUTENT. * (split into UT_CLOSE and UT_UNSORTED) */ #include #include #include #include "config.h" #include "screen.h" #include "extern.h" extern struct display *display; extern struct win *fore; extern char *LoginName; #ifdef NETHACK extern nethackflag; #endif #ifdef UTNOKEEP # define UT_CLOSE # define UT_UNSORTED #endif #ifdef UTMPOK static slot_t TtyNameSlot __P((char *)); static int utmpok, utmpfd = -1; static char UtmpName[] = UTMPFILE; # if defined(GETUTENT) && !defined(SVR4) # if defined(hpux) /* cruel hpux release 8.0 */ # define pututline _pututline # endif /* hpux */ extern struct utmp *getutline(), *pututline(); # if defined(_SEQUENT_) extern struct utmp *ut_add_user(), *ut_delete_user(); extern char *ut_find_host(); # ifndef UTHOST # define UTHOST /* _SEQUENT_ has got ut_find_host() */ # endif # endif /* _SEQUENT_ */ # endif /* GETUTENT && !SVR4 */ # if !defined(GETUTENT) && !defined(UT_UNSORTED) # ifdef GETTTYENT # include # else struct ttyent { char *ty_name; }; static void setttyent __P((void)); static struct ttyent *getttyent __P((void)); # endif # endif /* !GETUTENT && !UT_UNSORTED */ #endif /* UTMPOK */ /* * SlotToggle - modify the utmp slot of the fore window. * * how > 0 do try to set a utmp slot. * how = 0 try to withdraw a utmp slot. * * w_slot = -1 window not logged in. * w_slot = 0 window not logged in, but should be logged in. * (unable to write utmp, or detached). */ void SlotToggle(how) int how; { debug1("SlotToggle %d\n", how); #ifdef UTMPOK if (how) { debug(" try to log in\n"); if ((fore->w_slot == (slot_t) -1) || (fore->w_slot == (slot_t) 0)) { # ifdef USRLIMIT if (CountUsers() >= USRLIMIT) Msg(0, "User limit reached."); else # endif { if (SetUtmp(fore) == 0) Msg(0, "This window is now logged in."); else Msg(0, "This window should now be logged in."); } } else Msg(0, "This window is already logged in."); } else { debug(" try to log out\n"); if (fore->w_slot == (slot_t) -1) Msg(0, "This window is already logged out\n"); else if (fore->w_slot == (slot_t) 0) { debug("What a relief! In fact, it was not logged in\n"); Msg(0, "This window is not logged in."); fore->w_slot = (slot_t) -1; } else { RemoveUtmp(fore); if (fore->w_slot != (slot_t) -1) Msg(0, "What? Cannot remove Utmp slot?"); else Msg(0, "This window is no longer logged in."); } } #else /* !UTMPOK */ # ifdef UTMPFILE Msg(0, "Unable to modify %s.\n", UTMPFILE); # else Msg(0, "Unable to modify utmp-database.\n"); # endif #endif } #ifdef UTMPOK void InitUtmp() { debug1("InitUtmp testing '%s'...\n", UtmpName); if ((utmpfd = open(UtmpName, O_RDWR)) == -1) { if (errno != EACCES) Msg(errno, UtmpName); debug("InitUtmp failed.\n"); utmpok = 0; return; } #ifdef GETUTENT close(utmpfd); /* it was just a test */ utmpfd = -1; #endif /* GETUTENT */ utmpok = 1; } #ifdef USRLIMIT int CountUsers() { # ifdef GETUTENT struct utmp *ut, *getutent(); # else /* GETUTENT */ struct utmp utmpbuf; # endif /* GETUTENT */ int UserCount; # ifdef UT_CLOSE InitUtmp(); # endif /* UT_CLOSE */ debug1("CountUsers() - utmpok=%d\n", utmpok); if (!utmpok) return 0; UserCount = 0; # ifdef GETUTENT setutent(); while (ut = getutent()) if (ut->ut_type == USER_PROCESS) UserCount++; # else /* GETUTENT */ (void) lseek(utmpfd, (off_t) 0, 0); while (read(utmpfd, &utmpbuf, sizeof(utmpbuf)) == sizeof(utmpbuf)) if (utmpbuf.ut_name[0] != '\0') UserCount++; # endif /* GETUTENT */ # ifdef UT_CLOSE close(utmpfd); # endif /* UT_CLOSE */ return UserCount; } #endif /* USRLIMIT */ /* * the utmp entry for tty is located and removed. * it is stored in D_utmp_logintty. */ void RemoveLoginSlot() { #ifdef GETUTENT struct utmp *uu; #endif /* GETUTENT */ struct utmp u; /* 'empty' slot that we write back */ #ifdef _SEQUENT_ char *p; #endif /* _SEQUENT_ */ ASSERT(display); debug("RemoveLoginSlot: removing your logintty\n"); D_loginslot = TtyNameSlot(D_usertty); if (D_loginslot == (slot_t)0 || D_loginslot == (slot_t)-1) return; #ifdef UT_CLOSE InitUtmp(); #endif /* UT_CLOSE */ if (!utmpok) { debug("RemoveLoginSlot: utmpok == 0\n"); return; } #ifdef _SEQUENT_ if (p = ut_find_host(D_loginslot)) strncpy(D_loginhost, p, sizeof(D_loginhost) - 1); D_loginhost[sizeof(D_loginhost) - 1] = 0; #endif /* _SEQUENT_ */ bzero((char *) &u, sizeof(u)); #ifdef GETUTENT setutent(); strncpy(u.ut_line, D_loginslot, sizeof(u.ut_line)); if ((uu = getutline(&u)) == 0) { Msg(0, "Utmp slot not found -> not removed"); return; } D_utmp_logintty = *uu; # ifdef _SEQUENT_ if (ut_delete_user(D_loginslot, uu->ut_pid, 0, 0) == 0) # else /* _SEQUENT_ */ u = *uu; /* * Ed Hill (ed-hill@uiowa.edu) screen-3.5.2 under AIX 3.2.5 * when you are down to a single screen, and you log out of it (thus exiting * screen), it leaves a stray entry in the utmp file. Seems better: * u.ut_type = EMPTY; */ u.ut_type = DEAD_PROCESS; u.ut_user[0] = '\0'; /* for Digital UNIX 3.2C, kilbi@rad.rwth-aachen.de */ u.ut_exit.e_termination = 0; u.ut_exit.e_exit= 0; if (pututline(&u) == 0) # endif /* _SEQUENT_ */ #else /* GETUTENT */ bzero((char *)&D_utmp_logintty, sizeof(u)); (void) lseek(utmpfd, (off_t) (D_loginslot * sizeof(u)), 0); if (read(utmpfd, (char *) &D_utmp_logintty, sizeof(u)) != sizeof(u)) { Msg(errno, "cannot read %s ??", UtmpName); sleep(1); } # ifdef UT_UNSORTED /* copy tty line */ bcopy((char *)&D_utmp_logintty, (char *)&u, sizeof(u)); bzero(u.ut_name, sizeof(u.ut_name)); bzero(u.ut_host, sizeof(u.ut_host)); # ifdef linux u.ut_type = DEAD_PROCESS; # endif # endif /* UT_UNSORTED */ (void) lseek(utmpfd, (off_t) (D_loginslot * sizeof(u)), 0); if (write(utmpfd, (char *) &u, sizeof(u)) != sizeof(u)) #endif /* GETUTENT */ { #ifdef NETHACK if (nethackflag) Msg(errno, "%s is too hard to dig in", UtmpName); else #endif /* NETHACK */ Msg(errno, "Could not write %s", UtmpName); } #ifdef UT_CLOSE close(utmpfd); #endif /* UT_CLOSE */ debug1(" slot %d zapped\n", (int)D_loginslot); } /* * D_utmp_logintty is reinserted into utmp */ void RestoreLoginSlot() { debug("RestoreLoginSlot()\n"); ASSERT(display); #ifdef UT_CLOSE InitUtmp(); #endif /* UT_CLOSE */ if (utmpok && D_loginslot != (slot_t)0 && D_loginslot != (slot_t)-1) { #ifdef GETUTENT # ifdef _SEQUENT_ int fail; debug1(" logging you in again (slot %s)\n", D_loginslot); /* * We have problems if we add the console and use ut_add_user() * because the id will be 'scon' instead of 'co'. So we * restore it with pututline(). The reason why we don't use * pututline all the time is that we want to set the host field. * Unfortunatelly this can only be done with ut_add_user(). */ if (*D_loginhost) { fail = (ut_add_user(D_utmp_logintty.ut_name, D_loginslot, D_utmp_logintty.ut_pid, *D_loginhost ? D_loginhost : (char *)0) == 0); } else { setutent(); fail = (pututline(&D_utmp_logintty) == 0); } if (fail) # else /* _SEQUENT_ */ debug1(" logging you in again (slot %s)\n", D_loginslot); setutent(); if (pututline(&D_utmp_logintty) == 0) # endif /* _SEQUENT */ #else /* GETUTENT */ debug1(" logging you in again (slot %d)\n", D_loginslot); # ifdef sequent /* * call sequent undocumented routine to count logins * and add utmp entry if possible */ if (add_utmp(D_loginslot, &D_utmp_logintty) == -1) # else /* sequent */ (void) lseek(utmpfd, (off_t) (D_loginslot * sizeof(struct utmp)), 0); if (write(utmpfd, (char *) &D_utmp_logintty, sizeof(struct utmp)) != sizeof(struct utmp)) # endif /* sequent */ #endif /* GETUTENT */ { #ifdef NETHACK if (nethackflag) Msg(errno, "%s is too hard to dig in", UtmpName); else #endif /* NETHACK */ Msg(errno,"Could not write %s", UtmpName); } } #ifdef UT_CLOSE close(utmpfd); #endif /* UT_CLOSE */ D_loginslot = (slot_t) 0; } /* * Construct a utmp entry for window wi. * the hostname field reflects what we know about the user (i.e. display) * location. If d_loginhost is not set, then he is local and we write * down the name of his terminal line; else he is remote and we keep * the hostname here. The letter S and the window id will be appended. * A saved utmp entry in wi->w_savut serves as a template, usually. */ int SetUtmp(wi) struct win *wi; { register char *p; register slot_t slot; #ifndef _SEQUENT_ char *line; #endif struct utmp u; int saved_ut; #ifdef UTHOST # ifdef _SEQUENT_ char host[100+5]; # else /* _SEQUENT_ */ char host[sizeof(D_utmp_logintty.ut_host)+5]; # endif /* _SEQUENT_ */ #endif /* UTHOST */ wi->w_slot = (slot_t)0; if (!utmpok) return -1; if ((slot = TtyNameSlot(wi->w_tty)) == (slot_t)0) { debug1("SetUtmp failed (tty %s).\n",wi->w_tty); return -1; } debug2("SetUtmp %d will get slot %d...\n", wi->w_number, (int)slot); #ifdef UT_CLOSE InitUtmp(); #endif /* UT_CLOSE */ bzero((char *) &u, sizeof(u)); if ((saved_ut = bcmp((char *) &wi->w_savut, (char *) &u, sizeof(u)))) /* restore original, of which we will adopt all fields but ut_host */ bcopy((char *) &wi->w_savut, (char *) &u, sizeof(u)); #ifdef UTHOST host[sizeof(host)-5] = '\0'; if (display) { # ifdef _SEQUENT_ strncpy(host, D_loginhost, sizeof(host) - 5); # else /* _SEQUENT */ strncpy(host, D_utmp_logintty.ut_host, sizeof(host) - 5); # endif /* _SEQUENT */ if (D_loginslot != (slot_t)0 && D_loginslot != (slot_t)-1 && host[0] != '\0') { /* * we want to set our ut_host field to something like * ":ttyhf:s.0" or * "faui45:s.0" or * "132.199.81.4:s.0" (even this may hurt..), but not * "faui45.informati"......:s.0 */ for (p = host; *p; p++) if ((*p < '0' || *p > '9') && (*p != '.')) break; if (*p) { for (p = host; *p; p++) if (*p == '.') { *p = '\0'; break; } } } else { strncpy(host + 1, stripdev(D_usertty), sizeof(host) - 6); host[0] = ':'; } } else strncpy(host, "local", sizeof(host) - 5); sprintf(host + strlen(host), ":S.%c", '0' + wi->w_number); debug1("rlogin hostname: '%s'\n", host); # if !defined(_SEQUENT_) && !defined(sequent) strncpy(u.ut_host, host, sizeof(u.ut_host)); # endif #endif /* UTHOST */ #ifdef _SEQUENT_ if (ut_add_user(saved_ut ? u.ut_user : LoginName, slot, saved_ut ? u.ut_pid : wi->w_pid, host) == 0) #else /* _SEQUENT_ */ if (!saved_ut) { /* make new utmp from scratch */ line = stripdev(wi->w_tty); # ifdef GETUTENT strncpy(u.ut_user, LoginName, sizeof(u.ut_user)); /* Now the tricky part... guess ut_id */ # ifdef sgi strncpy(u.ut_id, line + 3, sizeof(u.ut_id)); # else /* sgi */ # ifdef _IBMR2 strncpy(u.ut_id, line, sizeof(u.ut_id)); # else strncpy(u.ut_id, line + strlen(line) - 2, sizeof(u.ut_id)); # endif # endif /* sgi */ strncpy(u.ut_line, line, sizeof(u.ut_line)); u.ut_pid = wi->w_pid; u.ut_type = USER_PROCESS; (void) time((time_t *)&u.ut_time); } /* !saved_ut {-: */ setutent(); if (pututline(&u) == 0) # else /* GETUTENT */ strncpy(u.ut_line, line, sizeof(u.ut_line)); strncpy(u.ut_name, LoginName, sizeof(u.ut_name)); # if defined(linux) /* should have GETUTENT */ u.ut_type = USER_PROCESS; u.ut_pid = wi->w_pid; strncpy(u.ut_id, line + 3, sizeof(u.ut_id)); # endif /* linux */ (void) time((time_t *)&u.ut_time); /* cast needed for ultrix */ } /* !saved_ut */ # ifdef sequent /* * call sequent undocumented routine to count logins and * add utmp entry if possible */ if (add_utmp(slot, &u) == -1) # else /* sequent */ (void) lseek(utmpfd, (off_t) (slot * sizeof(u)), 0); if (write(utmpfd, (char *) &u, sizeof(u)) != sizeof(u)) # endif /* sequent */ # endif /* GETUTENT */ #endif /* _SEQUENT_ */ { #ifdef NETHACK if (nethackflag) Msg(errno, "%s is too hard to dig in", UtmpName); else #endif /* NETHACK */ Msg(errno,"Could not write %s", UtmpName); #ifdef UT_CLOSE close(utmpfd); #endif /* UT_CLOSE */ return -1; } debug("SetUtmp successful\n"); wi->w_slot = slot; #ifdef UT_CLOSE close(utmpfd); #endif /* UT_CLOSE */ bcopy((char *) &u, (char *) &wi->w_savut, sizeof(u)); return 0; } /* * if slot could be removed or was 0, wi->w_slot = -1; * else not changed. */ int RemoveUtmp(wi) struct win *wi; { #ifdef GETUTENT struct utmp *uu; #endif /* GETUTENT */ struct utmp u; slot_t slot; slot = wi->w_slot; #ifdef GETUTENT debug1("RemoveUtmp(%s)\n", (slot == (slot_t) 0) ? "no slot (0)":((slot == (slot_t) -1) ? "no slot (-1)" : slot)); #else /* GETUTENT */ debug1("RemoveUtmp(wi.slot: %d)\n", slot); #endif /* GETUTENT */ #ifdef UT_CLOSE InitUtmp(); #endif /* UT_CLOSE */ if (!utmpok) return -1; if (slot == (slot_t) 0 || slot == (slot_t) -1) { debug1("There is no utmp-slot to be removed(%d)\n", (int)slot); wi->w_slot = (slot_t) -1; return 0; } bzero((char *) &u, sizeof(u)); #ifdef GETUTENT setutent(); # ifdef sgi bcopy((char *) &wi->w_savut, (char *) &u, sizeof(u)); uu = &u; # else strncpy(u.ut_line, slot, sizeof(u.ut_line)); if ((uu = getutline(&u)) == 0) { Msg(0, "Utmp slot not found -> not removed"); return -1; } bcopy((char *)uu, (char *)&wi->w_savut, sizeof(wi->w_savut)); # endif # ifdef _SEQUENT_ if (ut_delete_user(slot, uu->ut_pid, 0, 0) == 0) # else /* _SEQUENT_ */ u = *uu; u.ut_type = DEAD_PROCESS; u.ut_exit.e_termination = 0; u.ut_exit.e_exit= 0; if (pututline(&u) == 0) # endif /* _SEQUENT_ */ #else /* GETUTENT */ (void) lseek(utmpfd, (off_t) (slot * sizeof(u)), 0); if (read(utmpfd, (char *) &wi->w_savut, sizeof(u)) != sizeof(u)) { bzero((char *)&wi->w_savut, sizeof(wi->w_savut)); Msg(errno, "cannot read %s?", UtmpName); sleep(1); } # ifdef UT_UNSORTED bcopy((char *)&wi->w_savut, (char *)&u, sizeof(u)); bzero(u.ut_name, sizeof(u.ut_name)); bzero(u.ut_host, sizeof(u.ut_host)); # ifdef linux u.ut_type = DEAD_PROCESS; # endif # endif /* UT_UNSORTED */ (void) lseek(utmpfd, (off_t) (slot * sizeof(u)), 0); if (write(utmpfd, (char *) &u, sizeof(u)) != sizeof(u)) #endif /* GETUTENT */ { #ifdef NETHACK if (nethackflag) Msg(errno, "%s is too hard to dig in", UtmpName); else #endif /* NETHACK */ Msg(errno,"Could not write %s", UtmpName); #ifdef UT_CLOSE close(utmpfd); #endif /* UT_CLOSE */ return -1; } debug("RemoveUtmp successfull\n"); wi->w_slot = (slot_t) -1; #ifdef UT_CLOSE close(utmpfd); #endif /* UT_CLOSE */ return 0; } /* * TtyNameSlot: * return an index, where the named tty is found in utmp. */ static slot_t TtyNameSlot(nam) char *nam; { char *name; register slot_t slot; #ifdef UT_UNSORTED struct utmp u; #else # ifndef GETUTENT register struct ttyent *tp; # endif /* GETUTENT */ #endif /* UT_UNSORTED */ debug1("TtyNameSlot(%s)\n", nam); #ifdef UT_CLOSE InitUtmp(); #endif /* UT_CLOSE */ if (!utmpok || nam == 0) return (slot_t)0; name = stripdev(nam); #ifdef GETUTENT slot = name; #else /* GETUTENT */ # ifdef UT_UNSORTED slot = 0; (void) lseek(utmpfd, (off_t) 0, 0); while ((read(utmpfd, (char *)&u, sizeof(u)) == sizeof(u)) && (strcmp(u.ut_line, name))) slot++; # else /* UT_UNSORTED*/ slot = 1; setttyent(); while ((tp = getttyent()) != 0 && strcmp(name, tp->ty_name) != 0) slot++; # endif /* UTNOKEEP */ #endif /* GETUTENT */ #ifdef UT_CLOSE close(utmpfd); #endif return slot; } #if !defined(GETTTYENT) && !defined(GETUTENT) && !defined(UT_UNSORTED) /* * Cheap plastic imitation of ttyent routines. */ static char *tt, *ttnext; static char ttys[] = "/etc/ttys"; static void setttyent() { if (ttnext == 0) { struct stat s; register int f; register char *p, *ep; if ((f = open(ttys, O_RDONLY)) == -1 || fstat(f, &s) == -1) Panic(errno, ttys); if ((tt = malloc((unsigned) s.st_size + 1)) == 0) Panic(0, strnomem); if (read(f, tt, s.st_size) != s.st_size) Panic(errno, ttys); close(f); for (p = tt, ep = p + s.st_size; p < ep; p++) if (*p == '\n') *p = '\0'; *p = '\0'; } ttnext = tt; } static struct ttyent * getttyent() { static struct ttyent t; if (*ttnext == '\0') return NULL; t.ty_name = ttnext + 2; ttnext += strlen(ttnext) + 1; return &t; } #endif /* !GETTTYENT && !GETUTENT && !UT_UNSORTED*/ #endif /* UTMPOK */ /********************************************************************* * * getlogin() replacement (for SVR4 machines) */ # if defined(BUGGYGETLOGIN) && defined(UTMP_FILE) char * getlogin() { char *tty; #ifdef utmp # undef utmp #endif struct utmp u; static char retbuf[sizeof(u.ut_user)+1]; int fd; for (fd = 0; fd <= 2 && (tty = ttyname(fd)) == NULL; fd++) ; if ((tty == NULL) || ((fd = open(UTMP_FILE, O_RDONLY)) < 0)) return NULL; tty = stripdev(tty); retbuf[0] = '\0'; while (read(fd, (char *)&u, sizeof(struct utmp)) == sizeof(struct utmp)) { if (!strncmp(tty, u.ut_line, sizeof(u.ut_line))) { strncpy(retbuf, u.ut_user, sizeof(u.ut_user)); retbuf[sizeof(u.ut_user)] = '\0'; if (u.ut_type == USER_PROCESS) break; } } close(fd); return *retbuf ? retbuf : NULL; } # endif /* BUGGYGETLOGIN */