1 /* Copyright (c) 2008, 2009
2 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Micah Cowan (micah@cowan.name)
5 * Sadrul Habib Chowdhury (sadrul@users.sourceforge.net)
6 * Copyright (c) 1993-2002, 2003, 2005, 2006, 2007
7 * Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
8 * Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
9 * Copyright (c) 1987 Oliver Laumann
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 3, or (at your option)
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program (see the file COPYING); if not, see
23 * http://www.gnu.org/licenses/, or contact Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
26 ****************************************************************
29 #include <sys/types.h>
42 extern struct display
*display
;
44 extern struct win
*windows
;
46 extern struct win
*fore
;
47 extern char *LoginName
;
48 extern int real_uid
, eff_uid
;
52 * UTNOKEEP: A (ugly) hack for apollo that does two things:
53 * 1) Always close and reopen the utmp file descriptor. (I don't know
54 * for what reason this is done...)
55 * 2) Implement an unsorted utmp file much like GETUTENT.
56 * (split into UT_CLOSE and UT_UNSORTED)
67 # define UT_CLOSE endutent()
74 * we have a suid-root helper app that changes the utmp for us
75 * (won't work for login-slots)
77 #if (defined(sun) && defined(SVR4) && defined(GETUTENT)) || defined(HAVE_UTEMPTER)
86 static slot_t TtyNameSlot
__P((char *));
87 static void makeuser
__P((struct utmp
*, char *, char *, int));
88 static void makedead
__P((struct utmp
*));
89 static int pututslot
__P((slot_t
, struct utmp
*, char *, struct win
*));
90 static struct utmp
*getutslot
__P((slot_t
));
92 static struct utmp
*getutent
__P((void));
93 static void endutent
__P((void));
94 static int initutmp
__P((void));
95 static void setutent
__P((void));
97 #if defined(linux) && defined(GETUTENT)
98 static struct utmp
*xpututline
__P((struct utmp
*utmp
));
99 # define pututline xpututline
104 static char UtmpName
[] = UTMPFILE
;
106 static int utmpfd
= -1;
110 # if defined(GETUTENT) && (!defined(SVR4) || defined(__hpux)) && ! defined(__CYGWIN__)
111 # if defined(hpux) /* cruel hpux release 8.0 */
112 # define pututline _pututline
114 extern struct utmp
*getutline(), *pututline();
115 # if defined(_SEQUENT_)
116 extern struct utmp
*ut_add_user(), *ut_delete_user();
117 extern char *ut_find_host();
119 # define UTHOST /* _SEQUENT_ has ut_find_host() */
121 # endif /* _SEQUENT_ */
122 # endif /* GETUTENT && !SVR4 */
124 # if !defined(GETUTENT) && !defined(UT_UNSORTED)
128 struct ttyent
{ char *ty_name
; };
129 static void setttyent
__P((void));
130 static struct ttyent
*getttyent
__P((void));
132 # endif /* !GETUTENT && !UT_UNSORTED */
136 # define D_loginhost D_utmp_logintty.ut_host
140 # define D_loginhost ((char *)0)
148 * SlotToggle - modify the utmp slot of the fore window.
150 * how > 0 do try to set a utmp slot.
151 * how = 0 try to withdraw a utmp slot.
153 * w_slot = -1 window not logged in.
154 * w_slot = 0 window not logged in, but should be logged in.
155 * (unable to write utmp, or detached).
163 debug1("SlotToggle (!UTMPOK) %d\n", how
);
165 Msg(0, "Unable to modify %s.\n", UTMPFILE
);
167 Msg(0, "Unable to modify utmp-database.\n");
180 debug1("SlotToggle %d\n", how
);
181 if (fore
->w_type
!= W_TYPE_PTY
)
183 Msg(0, "Can only work with normal windows.\n");
188 debug(" try to log in\n");
189 if ((fore
->w_slot
== (slot_t
) -1) || (fore
->w_slot
== (slot_t
) 0))
192 if (CountUsers() >= USRLIMIT
)
194 Msg(0, "User limit reached.");
198 if (SetUtmp(fore
) == 0)
199 Msg(0, "This window is now logged in.");
201 Msg(0, "This window should now be logged in.");
202 WindowChanged(fore
, 'f');
205 Msg(0, "This window is already logged in.");
209 debug(" try to log out\n");
210 if (fore
->w_slot
== (slot_t
) -1)
211 Msg(0, "This window is already logged out\n");
212 else if (fore
->w_slot
== (slot_t
) 0)
214 debug("What a relief! In fact, it was not logged in\n");
215 Msg(0, "This window is not logged in.");
216 fore
->w_slot
= (slot_t
) -1;
221 if (fore
->w_slot
!= (slot_t
) -1)
222 Msg(0, "What? Cannot remove Utmp slot?");
224 Msg(0, "This window is no longer logged in.");
228 WindowChanged(fore
, 'f');
236 /* CAREFULUTMP: goodie for paranoid sysadmins: always leave one
244 if (!windows
) /* hopeless */
246 debug("CarefulUtmp counting slots\n");
247 for (p
= windows
; p
; p
= p
->w_next
)
248 if (p
->w_ptyfd
>= 0 && p
->w_slot
!= (slot_t
)-1)
249 return; /* found one, nothing to do */
251 debug("CarefulUtmp: no slots, log one in again.\n");
252 for (p
= windows
; p
; p
= p
->w_next
)
253 if (p
->w_ptyfd
>= 0) /* no zombies please */
256 return; /* really hopeless */
258 Msg(0, "Window %d is now logged in.\n", p
->w_number
);
260 #endif /* CAREFULUTMP */
266 debug1("InitUtmp testing '%s'...\n", UtmpName
);
268 if ((utmpfd
= open(UtmpName
, O_RDWR
)) == -1)
271 Msg(errno
, UtmpName
);
272 debug("InitUtmp failed.\n");
277 close(utmpfd
); /* it was just a test */
279 # endif /* GETUTENT */
280 #endif /* UTMP_HELPER */
292 debug1("CountUsers() - utmpok=%d\n", utmpok
);
297 while (ut
= getutent())
303 #endif /* USRLIMIT */
308 * the utmp entry for tty is located and removed.
309 * it is stored in D_utmp_logintty.
317 debug("RemoveLoginSlot: removing your logintty\n");
318 D_loginslot
= TtyNameSlot(D_usertty
);
319 if (D_loginslot
== (slot_t
)0 || D_loginslot
== (slot_t
)-1)
322 if (eff_uid
) /* helpers can't do login slots. sigh. */
328 debug("RemoveLoginSlot: utmpok == 0\n");
335 if ((p
= ut_find_host(D_loginslot
)) != 0)
336 strncpy(D_loginhost
, p
, sizeof(D_loginhost
) - 1);
337 D_loginhost
[sizeof(D_loginhost
) - 1] = 0;
339 #endif /* _SEQUENT_ */
341 if ((uu
= getutslot(D_loginslot
)) == 0)
343 debug("Utmp slot not found -> not removed");
348 D_utmp_logintty
= *uu
;
351 if (pututslot(D_loginslot
, &u
, (char *)0, (struct win
*)0) == 0)
356 debug1(" slot %d zapped\n", (int)D_loginslot
);
357 if (D_loginslot
== (slot_t
)0)
359 /* couldn't remove slot, do a 'mesg n' at least. */
362 debug("couln't zap slot -> do mesg n\n");
364 if ((tty
= ttyname(D_userfd
)) && stat(tty
, &stb
) == 0 && (int)stb
.st_uid
== real_uid
&& ((int)stb
.st_mode
& 0777) != 0666)
366 D_loginttymode
= (int)stb
.st_mode
& 0777;
367 chmod(D_usertty
, stb
.st_mode
& 0600);
373 * D_utmp_logintty is reinserted into utmp
380 debug("RestoreLoginSlot()\n");
382 if (utmpok
&& D_loginslot
!= (slot_t
)0 && D_loginslot
!= (slot_t
)-1)
384 debug1(" logging you in again (slot %#x)\n", (int)D_loginslot
);
385 if (pututslot(D_loginslot
, &D_utmp_logintty
, D_loginhost
, (struct win
*)0) == 0)
386 Msg(errno
,"Could not write %s", UtmpName
);
389 D_loginslot
= (slot_t
)0;
390 if (D_loginttymode
&& (tty
= ttyname(D_userfd
)))
391 chmod(tty
, D_loginttymode
);
397 * Construct a utmp entry for window wi.
398 * the hostname field reflects what we know about the user (display)
399 * location. If d_loginhost is not set, then he is local and we write
400 * down the name of his terminal line; else he is remote and we keep
401 * the hostname here. The letter S and the window id will be appended.
402 * A saved utmp entry in wi->w_savut serves as a template, usually.
409 register slot_t slot
;
414 char host
[sizeof(D_loginhost
) + 15];
419 wi
->w_slot
= (slot_t
)0;
420 if (!utmpok
|| wi
->w_type
!= W_TYPE_PTY
)
422 if ((slot
= TtyNameSlot(wi
->w_tty
)) == (slot_t
)0)
424 debug1("SetUtmp failed (tty %s).\n",wi
->w_tty
);
427 debug2("SetUtmp %d will get slot %d...\n", wi
->w_number
, (int)slot
);
429 bzero((char *)&u
, sizeof(u
));
430 if ((saved_ut
= bcmp((char *) &wi
->w_savut
, (char *)&u
, sizeof(u
))))
431 /* restore original, of which we will adopt all fields but ut_host */
432 bcopy((char *)&wi
->w_savut
, (char *) &u
, sizeof(u
));
435 makeuser(&u
, stripdev(wi
->w_tty
), LoginName
, wi
->w_pid
);
438 host
[sizeof(host
) - 15] = '\0';
441 strncpy(host
, D_loginhost
, sizeof(host
) - 15);
442 if (D_loginslot
!= (slot_t
)0 && D_loginslot
!= (slot_t
)-1 && host
[0] != '\0')
445 * we want to set our ut_host field to something like
448 * "132.199.81.4:s.0" (even this may hurt..), but not
449 * "faui45.informati"......:s.0
450 * HPUX uses host:0.0, so chop at "." and ":" (Eric Backus)
452 for (p
= host
; *p
; p
++)
453 if ((*p
< '0' || *p
> '9') && (*p
!= '.'))
457 for (p
= host
; *p
; p
++)
458 if (*p
== '.' || (*p
== ':' && p
!= host
))
467 strncpy(host
+ 1, stripdev(D_usertty
), sizeof(host
) - 15 - 1);
472 strncpy(host
, "local", sizeof(host
) - 15);
474 sprintf(host
+ strlen(host
), ":S.%d", wi
->w_number
);
475 debug1("rlogin hostname: '%s'\n", host
);
477 # if !defined(_SEQUENT_) && !defined(sequent)
478 strncpy(u
.ut_host
, host
, sizeof(u
.ut_host
));
482 if (pututslot(slot
, &u
, host
, wi
) == 0)
484 Msg(errno
,"Could not write %s", UtmpName
);
488 debug("SetUtmp successful\n");
490 bcopy((char *)&u
, (char *)&wi
->w_savut
, sizeof(u
));
496 * if slot could be removed or was 0, wi->w_slot = -1;
508 debug1("RemoveUtmp slot=%#x\n", slot
);
511 if (slot
== (slot_t
)0 || slot
== (slot_t
)-1)
513 wi
->w_slot
= (slot_t
)-1;
516 bzero((char *) &u
, sizeof(u
));
518 bcopy((char *)&wi
->w_savut
, (char *)&u
, sizeof(u
));
521 if ((uu
= getutslot(slot
)) == 0)
523 Msg(0, "Utmp slot not found -> not removed");
526 bcopy((char *)uu
, (char *)&wi
->w_savut
, sizeof(wi
->w_savut
));
530 if (pututslot(slot
, &u
, (char *)0, wi
) == 0)
532 Msg(errno
,"Could not write %s", UtmpName
);
536 debug("RemoveUtmp successfull\n");
537 wi
->w_slot
= (slot_t
)-1;
544 /*********************************************************************
546 * routines using the getut* api
551 #define SLOT_USED(u) (u->ut_type == USER_PROCESS)
558 bzero((char *)&u
, sizeof(u
));
559 strncpy(u
.ut_line
, slot
, sizeof(u
.ut_line
));
561 return getutline(&u
);
565 pututslot(slot
, u
, host
, wi
)
572 if (SLOT_USED(u
) && host
&& *host
)
573 return ut_add_user(u
.ut_name
, slot
, u
.ut_pid
, host
) != 0;
575 return ut_delete_user(slot
, u
.ut_pid
, 0, 0) != 0;
578 if (eff_uid
&& wi
->w_ptyfd
!= -1)
580 /* sigh, linux hackers made the helper functions void */
582 addToUtmp(wi
->w_tty
, host
, wi
->w_ptyfd
);
584 removeLineFromUtmp(wi
->w_tty
, wi
->w_ptyfd
);
585 return 1; /* pray for success */
590 return pututline(u
) != 0;
600 u
->ut_type
= DEAD_PROCESS
;
601 #if (!defined(linux) || defined(EMPTY)) && !defined(__CYGWIN__)
602 u
->ut_exit
.e_termination
= 0;
603 u
->ut_exit
.e_exit
= 0;
605 #if !defined(sun) || !defined(SVR4)
606 u
->ut_user
[0] = 0; /* for Digital UNIX, kilbi@rad.rwth-aachen.de */
611 makeuser(u
, line
, user
, pid
)
617 u
->ut_type
= USER_PROCESS
;
618 strncpy(u
->ut_user
, user
, sizeof(u
->ut_user
));
619 /* Now the tricky part... guess ut_id */
620 #if defined(sgi) || defined(linux)
621 strncpy(u
->ut_id
, line
+ 3, sizeof(u
->ut_id
));
624 strncpy(u
->ut_id
, line
, sizeof(u
->ut_id
));
626 strncpy(u
->ut_id
, line
+ strlen(line
) - 2, sizeof(u
->ut_id
));
629 strncpy(u
->ut_line
, line
, sizeof(u
->ut_line
));
631 /* must use temp variable because of NetBSD/sparc64, where
632 * ut_xtime is long(64) but time_t is int(32) */
641 return stripdev(nam
);
647 /*********************************************************************
649 * getut emulation for systems lacking the api
652 static struct utmp uent
;
654 #define SLOT_USED(u) (u.ut_name[0] != 0)
661 return (utmpfd
= open(UtmpName
, O_RDWR
)) >= 0;
668 (void)lseek(utmpfd
, (off_t
)0, 0);
682 if (utmpfd
< 0 && !initutmp())
684 if (read(utmpfd
, &uent
, sizeof(uent
)) != sizeof(uent
))
693 if (utmpfd
< 0 && !initutmp())
695 lseek(utmpfd
, (off_t
)(slot
* sizeof(struct utmp
)), 0);
696 if (read(utmpfd
, &uent
, sizeof(uent
)) != sizeof(uent
))
702 pututslot(slot
, u
, host
, wi
)
710 return add_utmp(slot
, u
) != -1;
712 if (utmpfd
< 0 && !initutmp())
714 lseek(utmpfd
, (off_t
)(slot
* sizeof(*u
)), 0);
715 if (write(utmpfd
, u
, sizeof(*u
)) != sizeof(*u
))
726 bzero(u
->ut_name
, sizeof(u
->ut_name
));
728 bzero(u
->ut_host
, sizeof(u
->ut_host
));
731 bzero((char *)u
, sizeof(*u
));
737 makeuser(u
, line
, user
, pid
)
743 strncpy(u
->ut_line
, line
, sizeof(u
->ut_line
));
744 strncpy(u
->ut_name
, user
, sizeof(u
->ut_name
));
759 line
= stripdev(nam
);
764 for (slot
= 0; getutent(); slot
++)
765 if (strcmp(uent
.ut_line
, line
) == 0)
771 while ((tp
= getttyent()) != 0 && strcmp(line
, tp
->ty_name
) != 0)
777 #endif /* GETUTENT */
781 /*********************************************************************
783 * Cheap plastic imitation of ttyent routines.
786 #if !defined(GETTTYENT) && !defined(GETUTENT) && !defined(UT_UNSORTED)
789 static char *tt
, *ttnext
;
790 static char ttys
[] = "/etc/ttys";
799 register char *p
, *ep
;
801 if ((f
= open(ttys
, O_RDONLY
)) == -1 || fstat(f
, &s
) == -1)
803 if ((tt
= malloc((unsigned) s
.st_size
+ 1)) == 0)
805 if (read(f
, tt
, s
.st_size
) != s
.st_size
)
808 for (p
= tt
, ep
= p
+ s
.st_size
; p
< ep
; p
++)
816 static struct ttyent
*
819 static struct ttyent t
;
823 t
.ty_name
= ttnext
+ 2;
824 ttnext
+= strlen(ttnext
) + 1;
828 #endif /* !GETTTYENT && !GETUTENT && !UT_UNSORTED*/
837 /*********************************************************************
839 * getlogin() replacement (for SVR4 machines)
842 # if defined(BUGGYGETLOGIN) && defined(UTMP_FILE)
851 static char retbuf
[sizeof(u
.ut_user
)+1];
854 for (fd
= 0; fd
<= 2 && (tty
= ttyname(fd
)) == NULL
; fd
++)
856 if ((tty
== NULL
) || ((fd
= open(UTMP_FILE
, O_RDONLY
)) < 0))
860 while (read(fd
, (char *)&u
, sizeof(struct utmp
)) == sizeof(struct utmp
))
862 if (!strncmp(tty
, u
.ut_line
, sizeof(u
.ut_line
)))
864 strncpy(retbuf
, u
.ut_user
, sizeof(u
.ut_user
));
865 retbuf
[sizeof(u
.ut_user
)] = '\0';
866 if (u
.ut_type
== USER_PROCESS
)
872 return *retbuf
? retbuf
: NULL
;
874 # endif /* BUGGYGETLOGIN */
876 #if defined(linux) && defined(GETUTENT)
879 /* aargh, linux' pututline returns void! */
889 return u
->ut_type
== DEAD_PROCESS
? u
: 0;
890 return u
->ut_type
== u2
->ut_type
? u
: 0;