summaryrefslogtreecommitdiffstats
path: root/utmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'utmp.c')
-rw-r--r--utmp.c893
1 files changed, 893 insertions, 0 deletions
diff --git a/utmp.c b/utmp.c
new file mode 100644
index 0000000..bb56e2c
--- /dev/null
+++ b/utmp.c
@@ -0,0 +1,893 @@
+/* Copyright (c) 2008, 2009
+ * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
+ * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
+ * Micah Cowan (micah@cowan.name)
+ * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
+ * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
+ * 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 3, 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, see
+ * https://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
+ *
+ ****************************************************************
+ */
+
+#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;
+
+
+/*
+ * 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)
+ */
+
+
+#ifdef UTNOKEEP
+# define UT_CLOSE
+# define UT_UNSORTED
+#endif
+
+#ifdef UT_CLOSE
+# undef UT_CLOSE
+# define UT_CLOSE endutent()
+#else
+# define UT_CLOSE
+#endif
+
+
+/*
+ * we have a suid-root helper app that changes the utmp for us
+ * (won't work for login-slots)
+ */
+#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)) && ! defined(__CYGWIN__)
+# 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 ut_find_host() */
+# endif
+# endif /* _SEQUENT_ */
+# endif /* GETUTENT && !SVR4 */
+
+# 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 /* !GETUTENT && !UT_UNSORTED */
+
+#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 /* 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).
+ */
+
+#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
+
+/* CAREFULUTMP: goodie for paranoid sysadmins: always leave one
+ * window logged in
+ */
+void
+CarefulUtmp()
+{
+ struct win *p;
+
+ if (!windows) /* hopeless */
+ 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; /* found one, nothing to do */
+
+ debug("CarefulUtmp: no slots, log one in again.\n");
+ for (p = windows; p; p = p->w_next)
+ if (p->w_ptyfd >= 0) /* no zombies please */
+ break;
+ if (!p)
+ return; /* really hopeless */
+ SetUtmp(p);
+ Msg(0, "Window %d is now logged in.\n", p->w_number);
+}
+#endif /* CAREFULUTMP */
+
+
+void
+InitUtmp()
+{
+ debug1("InitUtmp testing '%s'...\n", UtmpName);
+#ifndef UTMP_HELPER
+ if ((utmpfd = open(UtmpName, O_RDWR)) == -1)
+ {
+ if (errno != EACCES)
+ Msg(errno, "%s", UtmpName);
+ debug("InitUtmp failed.\n");
+ utmpok = 0;
+ return;
+ }
+# ifdef GETUTENT
+ close(utmpfd); /* it was just a test */
+ utmpfd = -1;
+# endif /* GETUTENT */
+#endif /* UTMP_HELPER */
+ 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 /* USRLIMIT */
+
+
+
+/*
+ * the utmp entry for tty is located and removed.
+ * it is stored in D_utmp_logintty.
+ */
+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) /* helpers can't do login slots. sigh. */
+#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 /* _SEQUENT_ */
+
+ 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)
+ {
+ /* couldn't remove slot, do a 'mesg n' at least. */
+ struct stat stb;
+ char *tty;
+ debug("couln't zap slot -> do mesg n\n");
+ D_loginttymode = 0;
+ if ((tty = GetPtsPathOrSymlink(D_userfd)) && stat(tty, &stb) == 0 && (int)stb.st_uid == real_uid && !CheckTtyname(tty) && ((int)stb.st_mode & 0777) != 0666)
+ {
+ D_loginttymode = (int)stb.st_mode & 0777;
+ chmod(D_usertty, stb.st_mode & 0600);
+ }
+ }
+}
+
+/*
+ * D_utmp_logintty is reinserted into utmp
+ */
+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 = GetPtsPathOrSymlink(D_userfd)) && !CheckTtyname(tty))
+ chmod(tty, D_loginttymode);
+}
+
+
+
+/*
+ * Construct a utmp entry for window wi.
+ * the hostname field reflects what we know about the user (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 slot_t slot;
+ struct utmp u;
+ int saved_ut;
+#ifdef UTHOST
+ char *p;
+ char host[sizeof(D_loginhost) + 15];
+#else
+ char *host = 0;
+#endif /* UTHOST */
+
+ 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))))
+ /* restore original, of which we will adopt all fields but ut_host */
+ 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')
+ {
+ /*
+ * 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
+ * HPUX uses host:0.0, so chop at "." and ":" (Eric Backus)
+ */
+ 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 /* UTHOST */
+
+ 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;
+}
+
+/*
+ * if slot could be removed or was 0, wi->w_slot = -1;
+ * else not changed.
+ */
+
+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;
+}
+
+
+
+/*********************************************************************
+ *
+ * routines using the getut* api
+ */
+
+#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 && wi->w_ptyfd != -1)
+ {
+ /* sigh, linux hackers made the helper functions void */
+ if (SLOT_USED(u))
+ addToUtmp(wi->w_tty, host, wi->w_ptyfd);
+ else
+ removeLineFromUtmp(wi->w_tty, wi->w_ptyfd);
+ return 1; /* pray for success */
+ }
+#endif
+ setutent();
+#ifndef __CYGWIN__
+ return pututline(u) != 0;
+#else
+ return 1;
+#endif
+}
+
+static void
+makedead(u)
+struct utmp *u;
+{
+ u->ut_type = DEAD_PROCESS;
+#if (!defined(linux) || defined(EMPTY)) && !defined(__CYGWIN__)
+ u->ut_exit.e_termination = 0;
+ u->ut_exit.e_exit = 0;
+#endif
+#if !defined(sun) || !defined(SVR4)
+ u->ut_user[0] = 0; /* for Digital UNIX, kilbi@rad.rwth-aachen.de */
+#endif
+}
+
+static void
+makeuser(u, line, user, pid)
+struct utmp *u;
+char *line, *user;
+int pid;
+{
+ time_t now;
+ u->ut_type = USER_PROCESS;
+ strncpy(u->ut_user, user, sizeof(u->ut_user));
+ /* Now the tricky part... guess ut_id */
+#if defined(sgi) || defined(linux)
+ 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 = pid;
+ /* must use temp variable because of NetBSD/sparc64, where
+ * ut_xtime is long(64) but time_t is int(32) */
+ (void)time(&now);
+ u->ut_time = now;
+}
+
+static slot_t
+TtyNameSlot(nam)
+char *nam;
+{
+ return stripdev(nam);
+}
+
+
+#else /* GETUTENT */
+
+/*********************************************************************
+ *
+ * getut emulation for systems lacking the api
+ */
+
+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;
+{
+ time_t now;
+ strncpy(u->ut_line, line, sizeof(u->ut_line));
+ strncpy(u->ut_name, user, sizeof(u->ut_name));
+ (void)time(&now);
+ u->ut_time = now;
+}
+
+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 /* GETUTENT */
+
+
+
+/*********************************************************************
+ *
+ * Cheap plastic imitation of ttyent routines.
+ */
+
+#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 /* !GETTTYENT && !GETUTENT && !UT_UNSORTED*/
+
+
+
+#endif /* UTMPOK */
+
+
+
+
+/*********************************************************************
+ *
+ * getlogin() replacement (for SVR4 machines)
+ */
+
+# 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 = GetPtsPathOrSymlink(fd)) == NULL; fd++)
+ ;
+ if ((tty == NULL) || CheckTtyname(tty) || ((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 */
+
+#if defined(linux) && defined(GETUTENT)
+# undef pututline
+
+/* aargh, linux' pututline returns void! */
+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
+