1/* Copyright (c) 1993-2002
2 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Copyright (c) 1987 Oliver Laumann
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program (see the file COPYING); if not, write to the
18 * Free Software Foundation, Inc.,
19 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
20 *
21 ****************************************************************
22 */
23
24#include <sys/types.h>
25#include <sys/stat.h>
26#include <fcntl.h>
27
28#include "config.h"
29#include "screen.h"
30#include "extern.h"
31
32#ifdef HAVE_UTEMPTER
33#include <utempter.h>
34#endif
35
36
37extern struct display *display;
38#ifdef CAREFULUTMP
39extern struct win *windows;
40#endif
41extern struct win *fore;
42extern char *LoginName;
43extern int real_uid, eff_uid;
44
45
46/*
47 *  UTNOKEEP: A (ugly) hack for apollo that does two things:
48 *    1) Always close and reopen the utmp file descriptor. (I don't know
49 *       for what reason this is done...)
50 *    2) Implement an unsorted utmp file much like GETUTENT.
51 *  (split into UT_CLOSE and UT_UNSORTED)
52 */
53
54
55#ifdef UTNOKEEP
56# define UT_CLOSE
57# define UT_UNSORTED
58#endif
59
60#ifdef UT_CLOSE
61# undef UT_CLOSE
62# define UT_CLOSE endutent()
63#else
64# define UT_CLOSE
65#endif
66
67
68/*
69 *  we have a suid-root helper app that changes the utmp for us
70 *  (won't work for login-slots)
71 */
72#if (defined(sun) && defined(SVR4) && defined(GETUTENT)) || defined(HAVE_UTEMPTER)
73# define UTMP_HELPER
74#endif
75
76
77
78#ifdef UTMPOK
79
80
81static slot_t TtyNameSlot __P((char *));
82static void makeuser __P((struct utmp *, char *, char *, int));
83static void makedead __P((struct utmp *));
84static int  pututslot __P((slot_t, struct utmp *, char *, struct win *));
85static struct utmp *getutslot __P((slot_t));
86#ifndef GETUTENT
87static struct utmp *getutent __P((void));
88static void endutent __P((void));
89static int  initutmp __P((void));
90static void setutent __P((void));
91#endif
92#if defined(linux) && defined(GETUTENT)
93static struct utmp *xpututline __P((struct utmp *utmp));
94# define pututline xpututline
95#endif
96
97
98static int utmpok;
99static char UtmpName[] = UTMPFILE;
100#ifndef UTMP_HELPER
101static int utmpfd = -1;
102#endif
103
104
105# if defined(GETUTENT) && (!defined(SVR4) || defined(__hpux))
106#  if defined(hpux) /* cruel hpux release 8.0 */
107#   define pututline _pututline
108#  endif /* hpux */
109extern struct utmp *getutline(), *pututline();
110#  if defined(_SEQUENT_)
111extern struct utmp *ut_add_user(), *ut_delete_user();
112extern char *ut_find_host();
113#   ifndef UTHOST
114#    define UTHOST		/* _SEQUENT_ has ut_find_host() */
115#   endif
116#  endif /* _SEQUENT_ */
117# endif /* GETUTENT && !SVR4 */
118
119# if !defined(GETUTENT) && !defined(UT_UNSORTED)
120#  ifdef GETTTYENT
121#   include <ttyent.h>
122#  else
123struct ttyent { char *ty_name; };
124static void           setttyent __P((void));
125static struct ttyent *getttyent __P((void));
126#  endif
127# endif /* !GETUTENT && !UT_UNSORTED */
128
129#ifndef _SEQUENT_
130# undef  D_loginhost
131# define D_loginhost D_utmp_logintty.ut_host
132#endif
133#ifndef UTHOST
134# undef  D_loginhost
135# define D_loginhost ((char *)0)
136#endif
137
138
139#endif /* UTMPOK */
140
141
142/*
143 * SlotToggle - modify the utmp slot of the fore window.
144 *
145 * how > 0	do try to set a utmp slot.
146 * how = 0	try to withdraw a utmp slot.
147 *
148 * w_slot = -1  window not logged in.
149 * w_slot = 0   window not logged in, but should be logged in.
150 *              (unable to write utmp, or detached).
151 */
152
153#ifndef UTMPOK
154void
155SlotToggle(how)
156int how;
157{
158  debug1("SlotToggle (!UTMPOK) %d\n", how);
159# ifdef UTMPFILE
160  Msg(0, "Unable to modify %s.\n", UTMPFILE);
161# else
162  Msg(0, "Unable to modify utmp-database.\n");
163# endif
164}
165#endif
166
167
168
169#ifdef UTMPOK
170
171void
172SlotToggle(how)
173int how;
174{
175  debug1("SlotToggle %d\n", how);
176  if (fore->w_type != W_TYPE_PTY)
177    {
178      Msg(0, "Can only work with normal windows.\n");
179      return;
180    }
181  if (how)
182    {
183      debug(" try to log in\n");
184      if ((fore->w_slot == (slot_t) -1) || (fore->w_slot == (slot_t) 0))
185	{
186#ifdef USRLIMIT
187          if (CountUsers() >= USRLIMIT)
188	    {
189              Msg(0, "User limit reached.");
190	      return;
191	    }
192#endif
193	  if (SetUtmp(fore) == 0)
194	    Msg(0, "This window is now logged in.");
195	  else
196	    Msg(0, "This window should now be logged in.");
197	  WindowChanged(fore, 'f');
198	}
199      else
200	Msg(0, "This window is already logged in.");
201    }
202  else
203    {
204      debug(" try to log out\n");
205      if (fore->w_slot == (slot_t) -1)
206	Msg(0, "This window is already logged out\n");
207      else if (fore->w_slot == (slot_t) 0)
208	{
209	  debug("What a relief! In fact, it was not logged in\n");
210	  Msg(0, "This window is not logged in.");
211	  fore->w_slot = (slot_t) -1;
212	}
213      else
214	{
215	  RemoveUtmp(fore);
216	  if (fore->w_slot != (slot_t) -1)
217	    Msg(0, "What? Cannot remove Utmp slot?");
218	  else
219	    Msg(0, "This window is no longer logged in.");
220#ifdef CAREFULUTMP
221	  CarefulUtmp();
222#endif
223	  WindowChanged(fore, 'f');
224	}
225    }
226}
227
228
229#ifdef CAREFULUTMP
230
231/* CAREFULUTMP: goodie for paranoid sysadmins: always leave one
232 * window logged in
233 */
234void
235CarefulUtmp()
236{
237  struct win *p;
238
239  if (!windows)			/* hopeless */
240    return;
241  debug("CarefulUtmp counting slots\n");
242  for (p = windows; p; p = p->w_next)
243    if (p->w_ptyfd >= 0 && p->w_slot != (slot_t)-1)
244      return;			/* found one, nothing to do */
245
246  debug("CarefulUtmp: no slots, log one in again.\n");
247  for (p = windows; p; p = p->w_next)
248    if (p->w_ptyfd >= 0)	/* no zombies please */
249      break;
250  if (!p)
251    return;			/* really hopeless */
252  SetUtmp(p);
253  Msg(0, "Window %d is now logged in.\n", p->w_number);
254}
255#endif /* CAREFULUTMP */
256
257
258void
259InitUtmp()
260{
261  debug1("InitUtmp testing '%s'...\n", UtmpName);
262#ifndef UTMP_HELPER
263  if ((utmpfd = open(UtmpName, O_RDWR)) == -1)
264    {
265      if (errno != EACCES)
266	Msg(errno, UtmpName);
267      debug("InitUtmp failed.\n");
268      utmpok = 0;
269      return;
270    }
271# ifdef GETUTENT
272  close(utmpfd);	/* it was just a test */
273  utmpfd = -1;
274# endif /* GETUTENT */
275#endif	/* UTMP_HELPER */
276  utmpok = 1;
277}
278
279
280#ifdef USRLIMIT
281int
282CountUsers()
283{
284  struct utmp *ut;
285  int UserCount;
286
287  debug1("CountUsers() - utmpok=%d\n", utmpok);
288  if (!utmpok)
289    return 0;
290  UserCount = 0;
291  setutent();
292  while (ut = getutent())
293    if (SLOT_USED(ut))
294      UserCount++;
295  UT_CLOSE;
296  return UserCount;
297}
298#endif /* USRLIMIT */
299
300
301
302/*
303 * the utmp entry for tty is located and removed.
304 * it is stored in D_utmp_logintty.
305 */
306void
307RemoveLoginSlot()
308{
309  struct utmp u, *uu;
310
311  ASSERT(display);
312  debug("RemoveLoginSlot: removing your logintty\n");
313  D_loginslot = TtyNameSlot(D_usertty);
314  if (D_loginslot == (slot_t)0 || D_loginslot == (slot_t)-1)
315    return;
316#ifdef UTMP_HELPER
317  if (eff_uid)	/* helpers can't do login slots. sigh. */
318#else
319  if (!utmpok)
320#endif
321    {
322      D_loginslot = 0;
323      debug("RemoveLoginSlot: utmpok == 0\n");
324    }
325  else
326    {
327#ifdef _SEQUENT_
328      {
329	char *p;
330	if ((p = ut_find_host(D_loginslot)) != 0)
331	  strncpy(D_loginhost, p, sizeof(D_loginhost) - 1);
332	D_loginhost[sizeof(D_loginhost) - 1] = 0;
333      }
334#endif /* _SEQUENT_ */
335
336      if ((uu = getutslot(D_loginslot)) == 0)
337	{
338	  debug("Utmp slot not found -> not removed");
339	  D_loginslot = 0;
340	}
341      else
342	{
343	  D_utmp_logintty = *uu;
344	  u = *uu;
345	  makedead(&u);
346	  if (pututslot(D_loginslot, &u, (char *)0, (struct win *)0) == 0)
347	    D_loginslot = 0;
348	}
349      UT_CLOSE;
350    }
351  debug1(" slot %d zapped\n", (int)D_loginslot);
352  if (D_loginslot == (slot_t)0)
353    {
354      /* couldn't remove slot, do a 'mesg n' at least. */
355      struct stat stb;
356      char *tty;
357      debug("couln't zap slot -> do mesg n\n");
358      D_loginttymode = 0;
359      if ((tty = ttyname(D_userfd)) && stat(tty, &stb) == 0 && (int)stb.st_uid == real_uid && ((int)stb.st_mode & 0777) != 0666)
360	{
361	  D_loginttymode = (int)stb.st_mode & 0777;
362	  chmod(D_usertty, stb.st_mode & 0600);
363	}
364    }
365}
366
367/*
368 * D_utmp_logintty is reinserted into utmp
369 */
370void
371RestoreLoginSlot()
372{
373  char *tty;
374
375  debug("RestoreLoginSlot()\n");
376  ASSERT(display);
377  if (utmpok && D_loginslot != (slot_t)0 && D_loginslot != (slot_t)-1)
378    {
379      debug1(" logging you in again (slot %#x)\n", (int)D_loginslot);
380      if (pututslot(D_loginslot, &D_utmp_logintty, D_loginhost, (struct win *)0) == 0)
381        Msg(errno,"Could not write %s", UtmpName);
382    }
383  UT_CLOSE;
384  D_loginslot = (slot_t)0;
385  if (D_loginttymode && (tty = ttyname(D_userfd)))
386    chmod(tty, D_loginttymode);
387}
388
389
390
391/*
392 * Construct a utmp entry for window wi.
393 * the hostname field reflects what we know about the user (display)
394 * location. If d_loginhost is not set, then he is local and we write
395 * down the name of his terminal line; else he is remote and we keep
396 * the hostname here. The letter S and the window id will be appended.
397 * A saved utmp entry in wi->w_savut serves as a template, usually.
398 */
399
400int
401SetUtmp(wi)
402struct win *wi;
403{
404  register slot_t slot;
405  struct utmp u;
406  int saved_ut;
407#ifdef UTHOST
408  char *p;
409  char host[sizeof(D_loginhost) + 15];
410#else
411  char *host = 0;
412#endif /* UTHOST */
413
414  wi->w_slot = (slot_t)0;
415  if (!utmpok || wi->w_type != W_TYPE_PTY)
416    return -1;
417  if ((slot = TtyNameSlot(wi->w_tty)) == (slot_t)0)
418    {
419      debug1("SetUtmp failed (tty %s).\n",wi->w_tty);
420      return -1;
421    }
422  debug2("SetUtmp %d will get slot %d...\n", wi->w_number, (int)slot);
423
424  bzero((char *)&u, sizeof(u));
425  if ((saved_ut = bcmp((char *) &wi->w_savut, (char *)&u, sizeof(u))))
426    /* restore original, of which we will adopt all fields but ut_host */
427    bcopy((char *)&wi->w_savut, (char *) &u, sizeof(u));
428
429  if (!saved_ut)
430    makeuser(&u, stripdev(wi->w_tty), LoginName, wi->w_pid);
431
432#ifdef UTHOST
433  host[sizeof(host) - 15] = '\0';
434  if (display)
435    {
436      strncpy(host, D_loginhost, sizeof(host) - 15);
437      if (D_loginslot != (slot_t)0 && D_loginslot != (slot_t)-1 && host[0] != '\0')
438	{
439	  /*
440	   * we want to set our ut_host field to something like
441	   * ":ttyhf:s.0" or
442	   * "faui45:s.0" or
443	   * "132.199.81.4:s.0" (even this may hurt..), but not
444	   * "faui45.informati"......:s.0
445	   * HPUX uses host:0.0, so chop at "." and ":" (Eric Backus)
446	   */
447	  for (p = host; *p; p++)
448	    if ((*p < '0' || *p > '9') && (*p != '.'))
449	      break;
450	  if (*p)
451	    {
452	      for (p = host; *p; p++)
453		if (*p == '.' || (*p == ':' && p != host))
454		  {
455		    *p = '\0';
456		    break;
457		  }
458	    }
459	}
460      else
461	{
462	  strncpy(host + 1, stripdev(D_usertty), sizeof(host) - 15 - 1);
463	  host[0] = ':';
464	}
465    }
466  else
467    strncpy(host, "local", sizeof(host) - 15);
468
469  sprintf(host + strlen(host), ":S.%d", wi->w_number);
470  debug1("rlogin hostname: '%s'\n", host);
471
472# if !defined(_SEQUENT_) && !defined(sequent)
473  strncpy(u.ut_host, host, sizeof(u.ut_host));
474# endif
475#endif /* UTHOST */
476
477  if (pututslot(slot, &u, host, wi) == 0)
478    {
479      Msg(errno,"Could not write %s", UtmpName);
480      UT_CLOSE;
481      return -1;
482    }
483  debug("SetUtmp successful\n");
484  wi->w_slot = slot;
485  bcopy((char *)&u, (char *)&wi->w_savut, sizeof(u));
486  UT_CLOSE;
487  return 0;
488}
489
490/*
491 * if slot could be removed or was 0,  wi->w_slot = -1;
492 * else not changed.
493 */
494
495int
496RemoveUtmp(wi)
497struct win *wi;
498{
499  struct utmp u, *uu;
500  slot_t slot;
501
502  slot = wi->w_slot;
503  debug1("RemoveUtmp slot=%#x\n", slot);
504  if (!utmpok)
505    return -1;
506  if (slot == (slot_t)0 || slot == (slot_t)-1)
507    {
508      wi->w_slot = (slot_t)-1;
509      return 0;
510    }
511  bzero((char *) &u, sizeof(u));
512#ifdef sgi
513  bcopy((char *)&wi->w_savut, (char *)&u, sizeof(u));
514  uu  = &u;
515#else
516  if ((uu = getutslot(slot)) == 0)
517    {
518      Msg(0, "Utmp slot not found -> not removed");
519      return -1;
520    }
521  bcopy((char *)uu, (char *)&wi->w_savut, sizeof(wi->w_savut));
522#endif
523  u = *uu;
524  makedead(&u);
525  if (pututslot(slot, &u, (char *)0, wi) == 0)
526    {
527      Msg(errno,"Could not write %s", UtmpName);
528      UT_CLOSE;
529      return -1;
530    }
531  debug("RemoveUtmp successfull\n");
532  wi->w_slot = (slot_t)-1;
533  UT_CLOSE;
534  return 0;
535}
536
537
538
539/*********************************************************************
540 *
541 *  routines using the getut* api
542 */
543
544#ifdef GETUTENT
545
546#define SLOT_USED(u) (u->ut_type == USER_PROCESS)
547
548static struct utmp *
549getutslot(slot)
550slot_t slot;
551{
552  struct utmp u;
553  bzero((char *)&u, sizeof(u));
554  strncpy(u.ut_line, slot, sizeof(u.ut_line));
555  setutent();
556  return getutline(&u);
557}
558
559static int
560pututslot(slot, u, host, wi)
561slot_t slot;
562struct utmp *u;
563char *host;
564struct win *wi;
565{
566#ifdef _SEQUENT_
567  if (SLOT_USED(u) && host && *host)
568    return ut_add_user(u.ut_name, slot, u.ut_pid, host) != 0;
569  if (!SLOT_USED(u))
570    return ut_delete_user(slot, u.ut_pid, 0, 0) != 0;
571#endif
572#ifdef HAVE_UTEMPTER
573  if (eff_uid && wi->w_ptyfd != -1)
574    {
575      /* sigh, linux hackers made the helper functions void */
576      if (SLOT_USED(u))
577	addToUtmp(wi->w_tty, host, wi->w_ptyfd);
578      else
579	removeLineFromUtmp(wi->w_tty, wi->w_ptyfd);
580      return 1;	/* pray for success */
581    }
582#endif
583  setutent();
584  return pututline(u) != 0;
585}
586
587static void
588makedead(u)
589struct utmp *u;
590{
591  u->ut_type = DEAD_PROCESS;
592#if !defined(linux) || defined(EMPTY)
593  u->ut_exit.e_termination = 0;
594  u->ut_exit.e_exit = 0;
595#endif
596#if !defined(sun) || !defined(SVR4)
597  u->ut_user[0] = 0;	/* for Digital UNIX, kilbi@rad.rwth-aachen.de */
598#endif
599}
600
601static void
602makeuser(u, line, user, pid)
603struct utmp *u;
604char *line, *user;
605int pid;
606{
607  u->ut_type = USER_PROCESS;
608  strncpy(u->ut_user, user, sizeof(u->ut_user));
609  /* Now the tricky part... guess ut_id */
610#if defined(sgi) || defined(linux)
611  strncpy(u->ut_id, line + 3, sizeof(u->ut_id));
612#else /* sgi */
613# ifdef _IBMR2
614  strncpy(u->ut_id, line, sizeof(u->ut_id));
615# else
616  strncpy(u->ut_id, line + strlen(line) - 2, sizeof(u->ut_id));
617# endif
618#endif /* sgi */
619  strncpy(u->ut_line, line, sizeof(u->ut_line));
620  u->ut_pid = pid;
621  (void)time((time_t *)&u->ut_time);
622}
623
624static slot_t
625TtyNameSlot(nam)
626char *nam;
627{
628  return stripdev(nam);
629}
630
631
632#else /* GETUTENT */
633
634/*********************************************************************
635 *
636 *  getut emulation for systems lacking the api
637 */
638
639static struct utmp uent;
640
641#define SLOT_USED(u) (u.ut_name[0] != 0)
642
643static int
644initutmp()
645{
646  if (utmpfd >= 0)
647    return 1;
648  return (utmpfd = open(UtmpName, O_RDWR)) >= 0;
649}
650
651static void
652setutent()
653{
654  if (utmpfd >= 0)
655    (void)lseek(utmpfd, (off_t)0, 0);
656}
657
658static void
659endutent()
660{
661  if (utmpfd >= 0)
662    close(utmpfd);
663  utmpfd = -1;
664}
665
666static struct utmp *
667getutent()
668{
669  if (utmpfd < 0 && !initutmp())
670    return 0;
671  if (read(utmpfd, &uent, sizeof(uent)) != sizeof(uent))
672    return 0;
673  return &uent;
674}
675
676static struct utmp *
677getutslot(slot)
678slot_t slot;
679{
680  if (utmpfd < 0 && !initutmp())
681    return 0;
682  lseek(utmpfd, (off_t)(slot * sizeof(struct utmp)), 0);
683  if (read(utmpfd, &uent, sizeof(uent)) != sizeof(uent))
684    return 0;
685  return &uent;
686}
687
688static int
689pututslot(slot, u, host, wi)
690slot_t slot;
691struct utmp *u;
692char *host;
693struct win *wi;
694{
695#ifdef sequent
696  if (SLOT_USED(u))
697    return add_utmp(slot, u) != -1;
698#endif
699  if (utmpfd < 0 && !initutmp())
700    return 0;
701  lseek(utmpfd, (off_t)(slot * sizeof(*u)), 0);
702  if (write(utmpfd, u, sizeof(*u)) != sizeof(*u))
703    return 0;
704  return 1;
705}
706
707
708static void
709makedead(u)
710struct utmp *u;
711{
712#ifdef UT_UNSORTED
713  bzero(u->ut_name, sizeof(u->ut_name));
714# ifdef UTHOST
715  bzero(u->ut_host, sizeof(u->ut_host));
716# endif
717#else
718  bzero((char *)u, sizeof(*u));
719#endif
720}
721
722
723static void
724makeuser(u, line, user, pid)
725struct utmp *u;
726char *line, *user;
727int pid;
728{
729  strncpy(u->ut_line, line, sizeof(u->ut_line));
730  strncpy(u->ut_name, user, sizeof(u->ut_name));
731  (void)time((time_t *)&u->ut_time);
732}
733
734static slot_t
735TtyNameSlot(nam)
736char *nam;
737{
738  slot_t slot;
739  char *line;
740#ifndef UT_UNSORTED
741  struct ttyent *tp;
742#endif
743
744  line = stripdev(nam);
745#ifdef UT_UNSORTED
746  setutent();
747  if (utmpfd < 0)
748    return -1;
749  for (slot = 0; getutent(); slot++)
750    if (strcmp(uent.ut_line, line) == 0)
751      break;
752  UT_CLOSE;
753#else
754  slot = 1;
755  setttyent();
756  while ((tp = getttyent()) != 0 && strcmp(line, tp->ty_name) != 0)
757    slot++;
758#endif
759  return slot;
760}
761
762#endif	/* GETUTENT */
763
764
765
766/*********************************************************************
767 *
768 *  Cheap plastic imitation of ttyent routines.
769 */
770
771#if !defined(GETTTYENT) && !defined(GETUTENT) && !defined(UT_UNSORTED)
772
773
774static char *tt, *ttnext;
775static char ttys[] = "/etc/ttys";
776
777static void
778setttyent()
779{
780  if (ttnext == 0)
781    {
782      struct stat s;
783      register int f;
784      register char *p, *ep;
785
786      if ((f = open(ttys, O_RDONLY)) == -1 || fstat(f, &s) == -1)
787	Panic(errno, ttys);
788      if ((tt = malloc((unsigned) s.st_size + 1)) == 0)
789	Panic(0, strnomem);
790      if (read(f, tt, s.st_size) != s.st_size)
791	Panic(errno, ttys);
792      close(f);
793      for (p = tt, ep = p + s.st_size; p < ep; p++)
794	if (*p == '\n')
795	  *p = '\0';
796      *p = '\0';
797    }
798  ttnext = tt;
799}
800
801static struct ttyent *
802getttyent()
803{
804  static struct ttyent t;
805
806  if (*ttnext == '\0')
807    return NULL;
808  t.ty_name = ttnext + 2;
809  ttnext += strlen(ttnext) + 1;
810  return &t;
811}
812
813#endif	/* !GETTTYENT && !GETUTENT && !UT_UNSORTED*/
814
815
816
817#endif /* UTMPOK */
818
819
820
821
822/*********************************************************************
823 *
824 *  getlogin() replacement (for SVR4 machines)
825 */
826
827# if defined(BUGGYGETLOGIN) && defined(UTMP_FILE)
828char *
829getlogin()
830{
831  char *tty = NULL;
832#ifdef utmp
833# undef utmp
834#endif
835  struct utmp u;
836  static char retbuf[sizeof(u.ut_user)+1];
837  int fd;
838
839  for (fd = 0; fd <= 2 && (tty = ttyname(fd)) == NULL; fd++)
840    ;
841  if ((tty == NULL) || ((fd = open(UTMP_FILE, O_RDONLY)) < 0))
842    return NULL;
843  tty = stripdev(tty);
844  retbuf[0] = '\0';
845  while (read(fd, (char *)&u, sizeof(struct utmp)) == sizeof(struct utmp))
846    {
847      if (!strncmp(tty, u.ut_line, sizeof(u.ut_line)))
848	{
849	  strncpy(retbuf, u.ut_user, sizeof(u.ut_user));
850	  retbuf[sizeof(u.ut_user)] = '\0';
851	  if (u.ut_type == USER_PROCESS)
852	    break;
853	}
854    }
855  close(fd);
856
857  return *retbuf ? retbuf : NULL;
858}
859# endif /* BUGGYGETLOGIN */
860
861#if defined(linux) && defined(GETUTENT)
862# undef pututline
863
864/* aargh, linux' pututline returns void! */
865struct utmp *
866xpututline(u)
867struct utmp *u;
868{
869  struct utmp *u2;
870  pututline(u);
871  setutent();
872  u2 = getutline(u);
873  if (u2 == 0)
874    return u->ut_type == DEAD_PROCESS ? u : 0;
875  return u->ut_type == u2->ut_type ? u : 0;
876}
877#endif
878
879