#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
#include "screen.h"
#include "extern.h"
#ifdef HAVE_UTEMPTER
#include <utempter.h>
#endif
extern struct display *display;
#ifdef CAREFULUTMP
extern struct win *windows;
#endif
extern struct win *fore;
extern char *LoginName;
extern int real_uid, eff_uid;
#ifdef UTNOKEEP
# define UT_CLOSE
# define UT_UNSORTED
#endif
#ifdef UT_CLOSE
# undef UT_CLOSE
# define UT_CLOSE endutent()
#else
# define UT_CLOSE
#endif
#if (defined(sun) && defined(SVR4) && defined(GETUTENT)) || defined(HAVE_UTEMPTER)
# define UTMP_HELPER
#endif
#ifdef UTMPOK
static slot_t TtyNameSlot __P((char *));
static void makeuser __P((struct utmp *, char *, char *, int));
static void makedead __P((struct utmp *));
static int pututslot __P((slot_t, struct utmp *, char *, struct win *));
static struct utmp *getutslot __P((slot_t));
#ifndef GETUTENT
static struct utmp *getutent __P((void));
static void endutent __P((void));
static int initutmp __P((void));
static void setutent __P((void));
#endif
#if defined(linux) && defined(GETUTENT)
static struct utmp *xpututline __P((struct utmp *utmp));
# define pututline xpututline
#endif
static int utmpok;
static char UtmpName[] = UTMPFILE;
#ifndef UTMP_HELPER
static int utmpfd = -1;
#endif
# if defined(GETUTENT) && (!defined(SVR4) || defined(__hpux))
# if defined(hpux)
# define pututline _pututline
# endif
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
# endif
# endif
# endif
# if !defined(GETUTENT) && !defined(UT_UNSORTED)
# ifdef GETTTYENT
# include <ttyent.h>
# else
struct ttyent { char *ty_name; };
static void setttyent __P((void));
static struct ttyent *getttyent __P((void));
# endif
# endif
#ifndef _SEQUENT_
# undef D_loginhost
# define D_loginhost D_utmp_logintty.ut_host
#endif
#ifndef UTHOST
# undef D_loginhost
# define D_loginhost ((char *)0)
#endif
#endif
#ifndef UTMPOK
void
SlotToggle(how)
int how;
{
debug1("SlotToggle (!UTMPOK) %d\n", how);
# ifdef UTMPFILE
Msg(0, "Unable to modify %s.\n", UTMPFILE);
# else
Msg(0, "Unable to modify utmp-database.\n");
# endif
}
#endif
#ifdef UTMPOK
void
SlotToggle(how)
int how;
{
debug1("SlotToggle %d\n", how);
if (fore->w_type != W_TYPE_PTY)
{
Msg(0, "Can only work with normal windows.\n");
return;
}
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.");
return;
}
#endif
if (SetUtmp(fore) == 0)
Msg(0, "This window is now logged in.");
else
Msg(0, "This window should now be logged in.");
WindowChanged(fore, 'f');
}
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.");
#ifdef CAREFULUTMP
CarefulUtmp();
#endif
WindowChanged(fore, 'f');
}
}
}
#ifdef CAREFULUTMP
void
CarefulUtmp()
{
struct win *p;
if (!windows)
return;
debug("CarefulUtmp counting slots\n");
for (p = windows; p; p = p->w_next)
if (p->w_ptyfd >= 0 && p->w_slot != (slot_t)-1)
return;
debug("CarefulUtmp: no slots, log one in again.\n");
for (p = windows; p; p = p->w_next)
if (p->w_ptyfd >= 0)
break;
if (!p)
return;
SetUtmp(p);
Msg(0, "Window %d is now logged in.\n", p->w_number);
}
#endif
void
InitUtmp()
{
debug1("InitUtmp testing '%s'...\n", UtmpName);
#ifndef UTMP_HELPER
if ((utmpfd = open(UtmpName, O_RDWR)) == -1)
{
if (errno != EACCES)
Msg(errno, UtmpName);
debug("InitUtmp failed.\n");
utmpok = 0;
return;
}
# ifdef GETUTENT
close(utmpfd);
utmpfd = -1;
# endif
#endif
utmpok = 1;
}
#ifdef USRLIMIT
int
CountUsers()
{
struct utmp *ut;
int UserCount;
debug1("CountUsers() - utmpok=%d\n", utmpok);
if (!utmpok)
return 0;
UserCount = 0;
setutent();
while (ut = getutent())
if (SLOT_USED(ut))
UserCount++;
UT_CLOSE;
return UserCount;
}
#endif
void
RemoveLoginSlot()
{
struct utmp u, *uu;
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 UTMP_HELPER
if (eff_uid)
#else
if (!utmpok)
#endif
{
D_loginslot = 0;
debug("RemoveLoginSlot: utmpok == 0\n");
}
else
{
#ifdef _SEQUENT_
{
char *p;
if ((p = ut_find_host(D_loginslot)) != 0)
strncpy(D_loginhost, p, sizeof(D_loginhost) - 1);
D_loginhost[sizeof(D_loginhost) - 1] = 0;
}
#endif
if ((uu = getutslot(D_loginslot)) == 0)
{
debug("Utmp slot not found -> not removed");
D_loginslot = 0;
}
else
{
D_utmp_logintty = *uu;
u = *uu;
makedead(&u);
if (pututslot(D_loginslot, &u, (char *)0, (struct win *)0) == 0)
D_loginslot = 0;
}
UT_CLOSE;
}
debug1(" slot %d zapped\n", (int)D_loginslot);
if (D_loginslot == (slot_t)0)
{
struct stat stb;
char *tty;
debug("couln't zap slot -> do mesg n\n");
D_loginttymode = 0;
if ((tty = ttyname(D_userfd)) && stat(tty, &stb) == 0 && (int)stb.st_uid == real_uid && ((int)stb.st_mode & 0777) != 0666)
{
D_loginttymode = (int)stb.st_mode & 0777;
chmod(D_usertty, stb.st_mode & 0600);
}
}
}
void
RestoreLoginSlot()
{
char *tty;
debug("RestoreLoginSlot()\n");
ASSERT(display);
if (utmpok && D_loginslot != (slot_t)0 && D_loginslot != (slot_t)-1)
{
debug1(" logging you in again (slot %#x)\n", (int)D_loginslot);
if (pututslot(D_loginslot, &D_utmp_logintty, D_loginhost, (struct win *)0) == 0)
Msg(errno,"Could not write %s", UtmpName);
}
UT_CLOSE;
D_loginslot = (slot_t)0;
if (D_loginttymode && (tty = ttyname(D_userfd)))
chmod(tty, D_loginttymode);
}
int
SetUtmp(wi)
struct win *wi;
{
register slot_t slot;
struct utmp u;
int saved_ut;
#ifdef UTHOST
char *p;
char host[sizeof(D_loginhost) + 15];
#else
char *host = 0;
#endif
wi->w_slot = (slot_t)0;
if (!utmpok || wi->w_type != W_TYPE_PTY)
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);
bzero((char *)&u, sizeof(u));
if ((saved_ut = bcmp((char *) &wi->w_savut, (char *)&u, sizeof(u))))
bcopy((char *)&wi->w_savut, (char *) &u, sizeof(u));
if (!saved_ut)
makeuser(&u, stripdev(wi->w_tty), LoginName, wi->w_pid);
#ifdef UTHOST
host[sizeof(host) - 15] = '\0';
if (display)
{
strncpy(host, D_loginhost, sizeof(host) - 15);
if (D_loginslot != (slot_t)0 && D_loginslot != (slot_t)-1 && host[0] != '\0')
{
for (p = host; *p; p++)
if ((*p < '0' || *p > '9') && (*p != '.'))
break;
if (*p)
{
for (p = host; *p; p++)
if (*p == '.' || (*p == ':' && p != host))
{
*p = '\0';
break;
}
}
}
else
{
strncpy(host + 1, stripdev(D_usertty), sizeof(host) - 15 - 1);
host[0] = ':';
}
}
else
strncpy(host, "local", sizeof(host) - 15);
sprintf(host + strlen(host), ":S.%d", 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
if (pututslot(slot, &u, host, wi) == 0)
{
Msg(errno,"Could not write %s", UtmpName);
UT_CLOSE;
return -1;
}
debug("SetUtmp successful\n");
wi->w_slot = slot;
bcopy((char *)&u, (char *)&wi->w_savut, sizeof(u));
UT_CLOSE;
return 0;
}
int
RemoveUtmp(wi)
struct win *wi;
{
struct utmp u, *uu;
slot_t slot;
slot = wi->w_slot;
debug1("RemoveUtmp slot=%#x\n", slot);
if (!utmpok)
return -1;
if (slot == (slot_t)0 || slot == (slot_t)-1)
{
wi->w_slot = (slot_t)-1;
return 0;
}
bzero((char *) &u, sizeof(u));
#ifdef sgi
bcopy((char *)&wi->w_savut, (char *)&u, sizeof(u));
uu = &u;
#else
if ((uu = getutslot(slot)) == 0)
{
Msg(0, "Utmp slot not found -> not removed");
return -1;
}
bcopy((char *)uu, (char *)&wi->w_savut, sizeof(wi->w_savut));
#endif
u = *uu;
makedead(&u);
if (pututslot(slot, &u, (char *)0, wi) == 0)
{
Msg(errno,"Could not write %s", UtmpName);
UT_CLOSE;
return -1;
}
debug("RemoveUtmp successfull\n");
wi->w_slot = (slot_t)-1;
UT_CLOSE;
return 0;
}
#ifdef GETUTENT
#define SLOT_USED(u) (u->ut_type == USER_PROCESS)
static struct utmp *
getutslot(slot)
slot_t slot;
{
struct utmp u;
bzero((char *)&u, sizeof(u));
strncpy(u.ut_line, slot, sizeof(u.ut_line));
setutent();
return getutline(&u);
}
static int
pututslot(slot, u, host, wi)
slot_t slot;
struct utmp *u;
char *host;
struct win *wi;
{
#ifdef _SEQUENT_
if (SLOT_USED(u) && host && *host)
return ut_add_user(u.ut_name, slot, u.ut_pid, host) != 0;
if (!SLOT_USED(u))
return ut_delete_user(slot, u.ut_pid, 0, 0) != 0;
#endif
#ifdef HAVE_UTEMPTER
if (eff_uid && wi->w_ptyfd != -1)
{
if (SLOT_USED(u))
addToUtmp(wi->w_tty, host, wi->w_ptyfd);
else
removeLineFromUtmp(wi->w_tty, wi->w_ptyfd);
return 1;
}
#endif
setutent();
return pututline(u) != 0;
}
static void
makedead(u)
struct utmp *u;
{
u->ut_type = DEAD_PROCESS;
#if !defined(linux) || defined(EMPTY)
u->ut_exit.e_termination = 0;
u->ut_exit.e_exit = 0;
#endif
#if !defined(sun) || !defined(SVR4)
u->ut_user[0] = 0;
#endif
}
static void
makeuser(u, line, user, pid)
struct utmp *u;
char *line, *user;
int pid;
{
u->ut_type = USER_PROCESS;
strncpy(u->ut_user, user, sizeof(u->ut_user));
#if defined(sgi) || defined(linux)
strncpy(u->ut_id, line + 3, sizeof(u->ut_id));
#else
# 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
strncpy(u->ut_line, line, sizeof(u->ut_line));
u->ut_pid = pid;
(void)time((time_t *)&u->ut_time);
}
static slot_t
TtyNameSlot(nam)
char *nam;
{
return stripdev(nam);
}
#else
static struct utmp uent;
#define SLOT_USED(u) (u.ut_name[0] != 0)
static int
initutmp()
{
if (utmpfd >= 0)
return 1;
return (utmpfd = open(UtmpName, O_RDWR)) >= 0;
}
static void
setutent()
{
if (utmpfd >= 0)
(void)lseek(utmpfd, (off_t)0, 0);
}
static void
endutent()
{
if (utmpfd >= 0)
close(utmpfd);
utmpfd = -1;
}
static struct utmp *
getutent()
{
if (utmpfd < 0 && !initutmp())
return 0;
if (read(utmpfd, &uent, sizeof(uent)) != sizeof(uent))
return 0;
return &uent;
}
static struct utmp *
getutslot(slot)
slot_t slot;
{
if (utmpfd < 0 && !initutmp())
return 0;
lseek(utmpfd, (off_t)(slot * sizeof(struct utmp)), 0);
if (read(utmpfd, &uent, sizeof(uent)) != sizeof(uent))
return 0;
return &uent;
}
static int
pututslot(slot, u, host, wi)
slot_t slot;
struct utmp *u;
char *host;
struct win *wi;
{
#ifdef sequent
if (SLOT_USED(u))
return add_utmp(slot, u) != -1;
#endif
if (utmpfd < 0 && !initutmp())
return 0;
lseek(utmpfd, (off_t)(slot * sizeof(*u)), 0);
if (write(utmpfd, u, sizeof(*u)) != sizeof(*u))
return 0;
return 1;
}
static void
makedead(u)
struct utmp *u;
{
#ifdef UT_UNSORTED
bzero(u->ut_name, sizeof(u->ut_name));
# ifdef UTHOST
bzero(u->ut_host, sizeof(u->ut_host));
# endif
#else
bzero((char *)u, sizeof(*u));
#endif
}
static void
makeuser(u, line, user, pid)
struct utmp *u;
char *line, *user;
int pid;
{
strncpy(u->ut_line, line, sizeof(u->ut_line));
strncpy(u->ut_name, user, sizeof(u->ut_name));
(void)time((time_t *)&u->ut_time);
}
static slot_t
TtyNameSlot(nam)
char *nam;
{
slot_t slot;
char *line;
#ifndef UT_UNSORTED
struct ttyent *tp;
#endif
line = stripdev(nam);
#ifdef UT_UNSORTED
setutent();
if (utmpfd < 0)
return -1;
for (slot = 0; getutent(); slot++)
if (strcmp(uent.ut_line, line) == 0)
break;
UT_CLOSE;
#else
slot = 1;
setttyent();
while ((tp = getttyent()) != 0 && strcmp(line, tp->ty_name) != 0)
slot++;
#endif
return slot;
}
#endif
#if !defined(GETTTYENT) && !defined(GETUTENT) && !defined(UT_UNSORTED)
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
#endif
# if defined(BUGGYGETLOGIN) && defined(UTMP_FILE)
char *
getlogin()
{
char *tty = NULL;
#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
#if defined(linux) && defined(GETUTENT)
# undef pututline
struct utmp *
xpututline(u)
struct utmp *u;
{
struct utmp *u2;
pututline(u);
setutent();
u2 = getutline(u);
if (u2 == 0)
return u->ut_type == DEAD_PROCESS ? u : 0;
return u->ut_type == u2->ut_type ? u : 0;
}
#endif