tc.who.c revision 167465
11573Srgrimes/* $Header: /p/tcsh/cvsroot/tcsh/tc.who.c,v 3.51 2006/03/03 22:08:45 amold Exp $ */
21573Srgrimes/*
31573Srgrimes * tc.who.c: Watch logins and logouts...
41573Srgrimes */
51573Srgrimes/*-
61573Srgrimes * Copyright (c) 1980, 1991 The Regents of the University of California.
71573Srgrimes * All rights reserved.
81573Srgrimes *
91573Srgrimes * Redistribution and use in source and binary forms, with or without
101573Srgrimes * modification, are permitted provided that the following conditions
111573Srgrimes * are met:
121573Srgrimes * 1. Redistributions of source code must retain the above copyright
131573Srgrimes *    notice, this list of conditions and the following disclaimer.
141573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
151573Srgrimes *    notice, this list of conditions and the following disclaimer in the
161573Srgrimes *    documentation and/or other materials provided with the distribution.
171573Srgrimes * 3. Neither the name of the University nor the names of its contributors
181573Srgrimes *    may be used to endorse or promote products derived from this software
191573Srgrimes *    without specific prior written permission.
201573Srgrimes *
211573Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221573Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231573Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241573Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251573Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261573Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271573Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281573Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291573Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301573Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311573Srgrimes * SUCH DAMAGE.
321573Srgrimes */
331573Srgrimes#include "sh.h"
341573Srgrimes
351573SrgrimesRCSID("$tcsh: tc.who.c,v 3.51 2006/03/03 22:08:45 amold Exp $")
361573Srgrimes
371573Srgrimes#include "tc.h"
381573Srgrimes
391573Srgrimes#ifndef HAVENOUTMP
4090045Sobrien/*
4190045Sobrien * kfk 26 Jan 1984 - for login watch functions.
421573Srgrimes */
431573Srgrimes#include <ctype.h>
441573Srgrimes
451573Srgrimes#ifdef HAVE_UTMPX_H
461573Srgrimes# include <utmpx.h>
471573Srgrimes/* I just redefine a few words here.  Changing every occurrence below
481573Srgrimes * seems like too much of work.  All UTMP functions have equivalent
491573Srgrimes * UTMPX counterparts, so they can be added all here when needed.
501573Srgrimes * Kimmo Suominen, Oct 14 1991
511573Srgrimes */
521573Srgrimes# if defined(__UTMPX_FILE) && !defined(UTMPX_FILE)
531573Srgrimes#  define TCSH_PATH_UTMP __UTMPX_FILE
541573Srgrimes# elif defined(_PATH_UTMPX)
551573Srgrimes#  define TCSH_PATH_UTMP _PATH_UTMPX
561573Srgrimes# elif defined(UTMPX_FILE)
571573Srgrimes#  define TCSH_PATH_UTMP UTMPX_FILE
581573Srgrimes# endif /* __UTMPX_FILE && !UTMPX_FILE */
591573Srgrimes# ifdef TCSH_PATH_UTMP
601573Srgrimes#  define utmp utmpx
611573Srgrimes#  if defined(HAVE_STRUCT_UTMP_UT_TV)
621573Srgrimes#   define ut_time ut_tv.tv_sec
638870Srgrimes#  elif defined(HAVE_STRUCT_UTMP_UT_XTIME)
641573Srgrimes#   define ut_time ut_xtime
651573Srgrimes#  endif
661573Srgrimes#  ifdef HAVE_STRUCT_UTMP_UT_USER
671573Srgrimes#   define ut_name ut_user
681573Srgrimes#  endif
691573Srgrimes#  ifdef HAVE_GETUTENT
701573Srgrimes#   define getutent getutxent
711573Srgrimes#   define setutent setutxent
721573Srgrimes#   define endutent endutxent
731573Srgrimes#  endif /* HAVE_GETUTENT */
741573Srgrimes# else
751573Srgrimes#  ifdef HAVE_UTMP_H
761573Srgrimes#   include <utmp.h>
771573Srgrimes#  endif /* WINNT_NATIVE */
781573Srgrimes# endif /* TCSH_PATH_UTMP */
791573Srgrimes#else /* !HAVE_UTMPX_H */
801573Srgrimes# ifdef HAVE_UTMP_H
8119276Sache#  include <utmp.h>
8219276Sache# endif /* WINNT_NATIVE */
831573Srgrimes#endif /* HAVE_UTMPX_H */
841573Srgrimes
851573Srgrimes#ifndef BROKEN_CC
861573Srgrimes# define UTNAMLEN	sizeof(((struct utmp *) 0)->ut_name)
871573Srgrimes# define UTLINLEN	sizeof(((struct utmp *) 0)->ut_line)
881573Srgrimes# ifdef HAVE_STRUCT_UTMP_UT_HOST
891573Srgrimes#  ifdef _SEQUENT_
901573Srgrimes#   define UTHOSTLEN	100
911573Srgrimes#  else
921573Srgrimes#   define UTHOSTLEN	sizeof(((struct utmp *) 0)->ut_host)
931573Srgrimes#  endif
941573Srgrimes# endif	/* HAVE_STRUCT_UTMP_UT_HOST */
951573Srgrimes#else
961573Srgrimes/* give poor cc a little help if it needs it */
971573Srgrimesstruct utmp __ut;
981573Srgrimes
991573Srgrimes# define UTNAMLEN	sizeof(__ut.ut_name)
1001573Srgrimes# define UTLINLEN	sizeof(__ut.ut_line)
1011573Srgrimes# ifdef HAVE_STRUCT_UTMP_UT_HOST
1021573Srgrimes#  ifdef _SEQUENT_
1031573Srgrimes#   define UTHOSTLEN	100
1041573Srgrimes#  else
1051573Srgrimes#   define UTHOSTLEN	sizeof(__ut.ut_host)
1061573Srgrimes#  endif
1071573Srgrimes# endif /* HAVE_STRUCT_UTMP_UT_HOST */
1081573Srgrimes#endif /* BROKEN_CC */
1091573Srgrimes
1101573Srgrimes#ifndef TCSH_PATH_UTMP
1111573Srgrimes# ifdef	UTMP_FILE
1121573Srgrimes#  define TCSH_PATH_UTMP UTMP_FILE
1131573Srgrimes# elif defined(_PATH_UTMP)
1141573Srgrimes#  define TCSH_PATH_UTMP _PATH_UTMP
1151573Srgrimes# else
1161573Srgrimes#  define TCSH_PATH_UTMP "/etc/utmp"
1171573Srgrimes# endif /* UTMP_FILE */
1181573Srgrimes#endif /* TCSH_PATH_UTMP */
1191573Srgrimes
1201573Srgrimes
1211573Srgrimesstruct who {
1221573Srgrimes    struct who *who_next;
1231573Srgrimes    struct who *who_prev;
1241573Srgrimes    char    who_name[UTNAMLEN + 1];
1251573Srgrimes    char    who_new[UTNAMLEN + 1];
1261573Srgrimes    char    who_tty[UTLINLEN + 1];
1271573Srgrimes#ifdef HAVE_STRUCT_UTMP_UT_HOST
1281573Srgrimes    char    who_host[UTHOSTLEN + 1];
1291573Srgrimes#endif /* HAVE_STRUCT_UTMP_UT_HOST */
1301573Srgrimes    time_t  who_time;
1311573Srgrimes    int     who_status;
1321573Srgrimes};
13390045Sobrien
13490045Sobrienstatic struct who whohead, whotail;
13590045Sobrienstatic time_t watch_period = 0;
13690045Sobrienstatic time_t stlast = 0;
13790045Sobrien#ifdef WHODEBUG
1381573Srgrimesstatic	void	debugwholist	(struct who *, struct who *);
13990045Sobrien#endif
1401573Srgrimesstatic	void	print_who	(struct who *);
14190045Sobrien
14290045Sobrien
14390045Sobrien#define ONLINE		01
14490045Sobrien#define OFFLINE		02
14590045Sobrien#define CHANGED		04
14690045Sobrien#define STMASK		07
14774963Speter#define ANNOUNCE	010
14890045Sobrien#define CLEARED		020
14990045Sobrien
15090045Sobrien/*
15190045Sobrien * Karl Kleinpaste, 26 Jan 1984.
1521573Srgrimes * Initialize the dummy tty list for login watch.
15390045Sobrien * This dummy list eliminates boundary conditions
1541573Srgrimes * when doing pointer-chase searches.
1551573Srgrimes */
1561573Srgrimesvoid
1571573Srgrimesinitwatch(void)
1581573Srgrimes{
15990045Sobrien    whohead.who_next = &whotail;
1601573Srgrimes    whotail.who_prev = &whohead;
1611573Srgrimes    stlast = 1;
1621573Srgrimes#ifdef WHODEBUG
16374469Sjlemon    debugwholist(NULL, NULL);
16474963Speter#endif /* WHODEBUG */
1651573Srgrimes}
1661573Srgrimes
1671573Srgrimesvoid
1681573Srgrimesresetwatch(void)
1691573Srgrimes{
1701573Srgrimes    watch_period = 0;
1711573Srgrimes    stlast = 0;
1721573Srgrimes}
17380525Smikeh
17474469Sjlemon/*
17580525Smikeh * Karl Kleinpaste, 26 Jan 1984.
17680525Smikeh * Watch /etc/utmp for login/logout changes.
17780525Smikeh */
17874469Sjlemonvoid
1791573Srgrimeswatch_login(int force)
1801573Srgrimes{
1811573Srgrimes    int     comp = -1, alldone;
1821573Srgrimes    int	    firsttime = stlast == 1;
1831573Srgrimes#ifdef HAVE_GETUTENT
18474963Speter    struct utmp *uptr;
1851573Srgrimes#else
1861573Srgrimes    int utmpfd;
1878870Srgrimes#endif
1881573Srgrimes    struct utmp utmp;
1891573Srgrimes    struct who *wp, *wpnew;
1901573Srgrimes    struct varent *v;
1911573Srgrimes    Char  **vp = NULL;
1921573Srgrimes    time_t  t, interval = MAILINTVL;
1931573Srgrimes    struct stat sta;
1941573Srgrimes#if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
1951573Srgrimes    char   *host, *ut_find_host();
1961573Srgrimes#endif
1971573Srgrimes#ifdef WINNT_NATIVE
1988870Srgrimes    static int ncbs_posted = 0;
1998870Srgrimes    USE(utmp);
2001573Srgrimes    USE(utmpfd);
2011573Srgrimes    USE(sta);
2021573Srgrimes    USE(wpnew);
2031573Srgrimes#endif /* WINNT_NATIVE */
20474469Sjlemon
2051573Srgrimes    /* stop SIGINT, lest our login list get trashed. */
20674469Sjlemon    pintr_disabled++;
2071573Srgrimes    cleanup_push(&pintr_disabled, disabled_cleanup);
2081573Srgrimes
2091573Srgrimes    v = adrof(STRwatch);
2101573Srgrimes    if ((v == NULL || v->vec == NULL) && !force) {
2111573Srgrimes	cleanup_until(&pintr_disabled);
2121573Srgrimes	return;			/* no names to watch */
2131573Srgrimes    }
21474963Speter    if (!force) {
21574963Speter	trim(vp = v->vec);
2161573Srgrimes	if (blklen(vp) % 2)		/* odd # args: 1st == # minutes. */
2171573Srgrimes	    interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL;
21874469Sjlemon    }
2191573Srgrimes    else
2201573Srgrimes	interval = 0;
2211573Srgrimes
2221573Srgrimes    (void) time(&t);
2231573Srgrimes#ifdef WINNT_NATIVE
2241573Srgrimes	/*
22574469Sjlemon	 * Since NCB_ASTATs take time, start em async at least 90 secs
2261573Srgrimes	 * before we are due -amol 6/5/97
2271573Srgrimes	 */
22874469Sjlemon	if (!ncbs_posted) {
2291573Srgrimes	    time_t tdiff = t - watch_period;
2301573Srgrimes	    if (!watch_period || ((tdiff  > 0) && (tdiff > (interval - 90)))) {
23174469Sjlemon		start_ncbs(vp);
2321573Srgrimes 		ncbs_posted = 1;
2331573Srgrimes	    }
2341573Srgrimes	}
2351573Srgrimes#endif /* WINNT_NATIVE */
2361573Srgrimes    if (t - watch_period < interval) {
2371573Srgrimes	cleanup_until(&pintr_disabled);
2381573Srgrimes	return;			/* not long enough yet... */
2391573Srgrimes    }
24074963Speter    watch_period = t;
24174963Speter#ifdef WINNT_NATIVE
2421573Srgrimes    ncbs_posted = 0;
2431573Srgrimes#else /* !WINNT_NATIVE */
24474469Sjlemon
2451573Srgrimes    /*
2461573Srgrimes     * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
2471573Srgrimes     * Don't open utmp all the time, stat it first...
2481573Srgrimes     */
24974963Speter    if (stat(TCSH_PATH_UTMP, &sta)) {
2501573Srgrimes	if (!force)
2511573Srgrimes	    xprintf(CGETS(26, 1,
2521573Srgrimes			  "cannot stat %s.  Please \"unset watch\".\n"),
2531573Srgrimes		    TCSH_PATH_UTMP);
25474963Speter	cleanup_until(&pintr_disabled);
2551573Srgrimes	return;
2561573Srgrimes    }
2571573Srgrimes    if (stlast == sta.st_mtime) {
2581573Srgrimes	cleanup_until(&pintr_disabled);
2591573Srgrimes	return;
2601573Srgrimes    }
2611573Srgrimes    stlast = sta.st_mtime;
2621573Srgrimes#ifdef HAVE_GETUTENT
2631573Srgrimes    setutent();
2648870Srgrimes#else
2651573Srgrimes    if ((utmpfd = xopen(TCSH_PATH_UTMP, O_RDONLY|O_LARGEFILE)) < 0) {
2661573Srgrimes	if (!force)
2671573Srgrimes	    xprintf(CGETS(26, 2,
2681573Srgrimes			  "%s cannot be opened.  Please \"unset watch\".\n"),
2691573Srgrimes		    TCSH_PATH_UTMP);
2701573Srgrimes	cleanup_until(&pintr_disabled);
2711573Srgrimes	return;
2721573Srgrimes    }
2731573Srgrimes    cleanup_push(&utmpfd, open_cleanup);
2741573Srgrimes#endif
2751573Srgrimes
2761573Srgrimes    /*
2771573Srgrimes     * xterm clears the entire utmp entry - mark everyone on the status list
2781573Srgrimes     * OFFLINE or we won't notice X "logouts"
2791573Srgrimes     */
2801573Srgrimes    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next)
28174469Sjlemon	wp->who_status = OFFLINE | CLEARED;
2821573Srgrimes
2831573Srgrimes    /*
2841573Srgrimes     * Read in the utmp file, sort the entries, and update existing entries or
2851573Srgrimes     * add new entries to the status list.
2861573Srgrimes     */
2871573Srgrimes#ifdef HAVE_GETUTENT
2881573Srgrimes    while ((uptr = getutent()) != NULL) {
2891573Srgrimes        memcpy(&utmp, uptr, sizeof (utmp));
2901573Srgrimes#else
2911573Srgrimes    while (xread(utmpfd, &utmp, sizeof utmp) == sizeof utmp) {
2928870Srgrimes#endif
2931573Srgrimes
2941573Srgrimes# ifdef DEAD_PROCESS
2951573Srgrimes#  ifndef IRIS4D
2961573Srgrimes	if (utmp.ut_type != USER_PROCESS)
2971573Srgrimes	    continue;
2981573Srgrimes#  else
2991573Srgrimes	/* Why is that? Cause the utmp file is always corrupted??? */
3001573Srgrimes	if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
3011573Srgrimes	    continue;
3021573Srgrimes#  endif /* IRIS4D */
3031573Srgrimes# endif /* DEAD_PROCESS */
3041573Srgrimes
3051573Srgrimes	if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
3061573Srgrimes	    continue;	/* completely void entry */
3071573Srgrimes# ifdef DEAD_PROCESS
3081573Srgrimes	if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
3091573Srgrimes	    continue;
3101573Srgrimes# endif /* DEAD_PROCESS */
3111573Srgrimes	wp = whohead.who_next;
3121573Srgrimes	while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0)
3131573Srgrimes	    wp = wp->who_next;/* find that tty! */
3141573Srgrimes
3151573Srgrimes	if (wp->who_next && comp == 0) {	/* found the tty... */
3161573Srgrimes	    if (utmp.ut_time < wp->who_time)
3178870Srgrimes	        continue;
3181573Srgrimes# ifdef DEAD_PROCESS
3191573Srgrimes	    if (utmp.ut_type == DEAD_PROCESS) {
3201573Srgrimes		wp->who_time = utmp.ut_time;
3211573Srgrimes		wp->who_status = OFFLINE;
3221573Srgrimes	    }
3231573Srgrimes	    else
3241573Srgrimes# endif /* DEAD_PROCESS */
3251573Srgrimes	    if (utmp.ut_name[0] == '\0') {
3261573Srgrimes		wp->who_time = utmp.ut_time;
3271573Srgrimes		wp->who_status = OFFLINE;
32874469Sjlemon	    }
3291573Srgrimes	    else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) {
3301573Srgrimes		/* someone is logged in */
3311573Srgrimes		wp->who_time = utmp.ut_time;
3321573Srgrimes		wp->who_status = ONLINE | ANNOUNCE;	/* same guy */
3331573Srgrimes	    }
3341573Srgrimes	    else {
3351573Srgrimes		(void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN);
3361573Srgrimes# ifdef HAVE_STRUCT_UTMP_UT_HOST
3371573Srgrimes#  ifdef _SEQUENT_
3381573Srgrimes		host = ut_find_host(wp->who_tty);
3391573Srgrimes		if (host)
3401573Srgrimes		    (void) strncpy(wp->who_host, host, UTHOSTLEN);
3411573Srgrimes		else
3421573Srgrimes		    wp->who_host[0] = 0;
3431573Srgrimes#  else
3441573Srgrimes		(void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN);
3451573Srgrimes#  endif
3461573Srgrimes# endif /* HAVE_STRUCT_UTMP_UT_HOST */
3471573Srgrimes		wp->who_time = utmp.ut_time;
34824158Simp		if (wp->who_name[0] == '\0')
3491573Srgrimes		    wp->who_status = ONLINE;
3501573Srgrimes		else
35124158Simp		    wp->who_status = CHANGED;
3521573Srgrimes	    }
3531573Srgrimes	}
3541573Srgrimes	else {		/* new tty in utmp */
3551573Srgrimes	    wpnew = xcalloc(1, sizeof *wpnew);
3561573Srgrimes	    (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN);
35724158Simp# ifdef HAVE_STRUCT_UTMP_UT_HOST
3581573Srgrimes#  ifdef _SEQUENT_
3591573Srgrimes	    host = ut_find_host(wpnew->who_tty);
3601573Srgrimes	    if (host)
3611573Srgrimes		(void) strncpy(wpnew->who_host, host, UTHOSTLEN);
36224158Simp	    else
36324158Simp		wpnew->who_host[0] = 0;
36424158Simp#  else
36524158Simp	    (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN);
36624158Simp#  endif
36724158Simp# endif /* HAVE_STRUCT_UTMP_UT_HOST */
3681573Srgrimes	    wpnew->who_time = utmp.ut_time;
3691573Srgrimes# ifdef DEAD_PROCESS
3701573Srgrimes	    if (utmp.ut_type == DEAD_PROCESS)
3711573Srgrimes		wpnew->who_status = OFFLINE;
3721573Srgrimes	    else
3738870Srgrimes# endif /* DEAD_PROCESS */
37428820Simp	    if (utmp.ut_name[0] == '\0')
37528820Simp		wpnew->who_status = OFFLINE;
37628820Simp	    else {
3771573Srgrimes		(void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN);
37833664Sjb		wpnew->who_status = ONLINE;
37933664Sjb	    }
38033664Sjb# ifdef WHODEBUG
38133664Sjb	    debugwholist(wpnew, wp);
38233664Sjb# endif /* WHODEBUG */
38328836Sache
38428836Sache	    wpnew->who_next = wp;	/* link in a new 'who' */
38528836Sache	    wpnew->who_prev = wp->who_prev;
38628836Sache	    wpnew->who_prev->who_next = wpnew;
38728836Sache	    wp->who_prev = wpnew;	/* linked in now */
3881573Srgrimes	}
3891573Srgrimes    }
3901573Srgrimes#ifdef HAVE_GETUTENT
3911573Srgrimes    endutent();
3921573Srgrimes#else
3931573Srgrimes    cleanup_until(&utmpfd);
3941573Srgrimes#endif
3951573Srgrimes# if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
3961573Srgrimes    endutent();
3971573Srgrimes# endif
3981573Srgrimes#endif /* !WINNT_NATIVE */
3991573Srgrimes
4001573Srgrimes    if (force || vp == NULL) {
4011573Srgrimes	cleanup_until(&pintr_disabled);
40224158Simp	return;
4031573Srgrimes    }
4048870Srgrimes
4051573Srgrimes    /*
40624158Simp     * The state of all logins is now known, so we can search the user's list
4071573Srgrimes     * of watchables to print the interesting ones.
40824158Simp     */
4091573Srgrimes    for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
4101573Srgrimes	 *(vp + 1) != NULL && **(vp + 1) != '\0';
4111573Srgrimes	 vp += 2) {		/* args used in pairs... */
4121573Srgrimes
4138870Srgrimes	if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
4141573Srgrimes	    alldone = 1;
4151573Srgrimes
4161573Srgrimes	for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
4171573Srgrimes	    if (wp->who_status & ANNOUNCE ||
4181573Srgrimes		(!eq(STRany, vp[0]) &&
4191573Srgrimes		 !Gmatch(str2short(wp->who_name), vp[0]) &&
4201573Srgrimes		 !Gmatch(str2short(wp->who_new),  vp[0])) ||
4211573Srgrimes		(!Gmatch(str2short(wp->who_tty),  vp[1]) &&
42274469Sjlemon		 !eq(STRany, vp[1])))
4231573Srgrimes		continue;	/* entry doesn't qualify */
4241573Srgrimes	    /* already printed or not right one to print */
42574469Sjlemon
4261573Srgrimes
4271573Srgrimes	    if (wp->who_status & CLEARED) {/* utmp entry was cleared */
4281573Srgrimes		wp->who_time = watch_period;
42974963Speter		wp->who_status &= ~CLEARED;
4301573Srgrimes	    }
43174963Speter
4321573Srgrimes	    if ((wp->who_status & OFFLINE) &&
4331573Srgrimes		(wp->who_name[0] != '\0')) {
4341573Srgrimes		if (!firsttime)
4351573Srgrimes		    print_who(wp);
4361573Srgrimes		wp->who_name[0] = '\0';
4371573Srgrimes		wp->who_status |= ANNOUNCE;
4381573Srgrimes		continue;
4391573Srgrimes	    }
4401573Srgrimes	    if (wp->who_status & ONLINE) {
4411573Srgrimes		if (!firsttime)
4421573Srgrimes		    print_who(wp);
4431573Srgrimes		(void) strcpy(wp->who_name, wp->who_new);
4441573Srgrimes		wp->who_status |= ANNOUNCE;
4451573Srgrimes		continue;
4461573Srgrimes	    }
4471573Srgrimes	    if (wp->who_status & CHANGED) {
4481573Srgrimes		if (!firsttime)
4491573Srgrimes		    print_who(wp);
4501573Srgrimes		(void) strcpy(wp->who_name, wp->who_new);
4511573Srgrimes		wp->who_status |= ANNOUNCE;
4521573Srgrimes		continue;
4531573Srgrimes	    }
4541573Srgrimes	}
4551573Srgrimes    }
4561573Srgrimes    cleanup_until(&pintr_disabled);
4571573Srgrimes}
4581573Srgrimes
4591573Srgrimes#ifdef WHODEBUG
4601573Srgrimesstatic void
4611573Srgrimesdebugwholist(struct who *new, struct who *wp)
4621573Srgrimes{
4631573Srgrimes    struct who *a;
4641573Srgrimes
4651573Srgrimes    a = whohead.who_next;
4661573Srgrimes    while (a->who_next != NULL) {
4671573Srgrimes	xprintf("%s/%s -> ", a->who_name, a->who_tty);
4681573Srgrimes	a = a->who_next;
4691573Srgrimes    }
4701573Srgrimes    xprintf("TAIL\n");
4718870Srgrimes    if (a != &whotail) {
4721573Srgrimes	xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
4731573Srgrimes	abort();
4741573Srgrimes    }
4751573Srgrimes    a = whotail.who_prev;
4761573Srgrimes    xprintf(CGETS(26, 4, "backward: "));
4771573Srgrimes    while (a->who_prev != NULL) {
4781573Srgrimes	xprintf("%s/%s -> ", a->who_name, a->who_tty);
4791573Srgrimes	a = a->who_prev;
4801573Srgrimes    }
4811573Srgrimes    xprintf("HEAD\n");
4821573Srgrimes    if (a != &whohead) {
4831573Srgrimes	xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
4841573Srgrimes	abort();
4851573Srgrimes    }
4861573Srgrimes    if (new)
48774469Sjlemon	xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty);
4881573Srgrimes    if (wp)
4891573Srgrimes	xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty);
4901573Srgrimes}
4918870Srgrimes#endif /* WHODEBUG */
4921573Srgrimes
4931573Srgrimes
4941573Srgrimesstatic void
4951573Srgrimesprint_who(struct who *wp)
4968870Srgrimes{
4978870Srgrimes#ifdef HAVE_STRUCT_UTMP_UT_HOST
4981573Srgrimes    Char   *cp = str2short(CGETS(26, 7, "%n has %a %l from %m."));
4991573Srgrimes#else
50074469Sjlemon    Char   *cp = str2short(CGETS(26, 8, "%n has %a %l."));
5018870Srgrimes#endif /* HAVE_STRUCT_UTMP_UT_HOST */
5021573Srgrimes    struct varent *vp = adrof(STRwho);
5031573Srgrimes    Char *str;
5041573Srgrimes
5051573Srgrimes    if (vp && vp->vec && vp->vec[0])
5061573Srgrimes	cp = vp->vec[0];
5071573Srgrimes
5081573Srgrimes    str = tprintf(FMT_WHO, cp, NULL, wp->who_time, wp);
5091573Srgrimes    cleanup_push(str, xfree);
5101573Srgrimes    for (cp = str; *cp;)
5111573Srgrimes	xputwchar(*cp++);
5121573Srgrimes    cleanup_until(str);
5131573Srgrimes    xputchar('\n');
5141573Srgrimes} /* end print_who */
51574469Sjlemon
5161573Srgrimes
5171573Srgrimeschar *
51874469Sjlemonwho_info(ptr_t ptr, int c)
5191573Srgrimes{
52074963Speter    struct who *wp = ptr;
5211573Srgrimes    char *wbuf;
5221573Srgrimes#ifdef HAVE_STRUCT_UTMP_UT_HOST
5231573Srgrimes    char *wb;
5241573Srgrimes    int flg;
52574963Speter    char *pb;
52674963Speter#endif /* HAVE_STRUCT_UTMP_UT_HOST */
5271573Srgrimes
5281573Srgrimes    switch (c) {
5291573Srgrimes    case 'n':		/* user name */
5301573Srgrimes	switch (wp->who_status & STMASK) {
5311573Srgrimes	case ONLINE:
5321573Srgrimes	case CHANGED:
5331573Srgrimes	    return strsave(wp->who_new);
5341573Srgrimes	case OFFLINE:
53574963Speter	    return strsave(wp->who_name);
53674963Speter	default:
5371573Srgrimes	    break;
53874469Sjlemon	}
5391573Srgrimes	break;
5401573Srgrimes
5411573Srgrimes    case 'a':
5421573Srgrimes	switch (wp->who_status & STMASK) {
5431573Srgrimes	case ONLINE:
5441573Srgrimes	    return strsave(CGETS(26, 9, "logged on"));
5451573Srgrimes	case OFFLINE:
5461573Srgrimes	    return strsave(CGETS(26, 10, "logged off"));
5471573Srgrimes	case CHANGED:
5481573Srgrimes	    return xasprintf(CGETS(26, 11, "replaced %s on"), wp->who_name);
5491573Srgrimes	default:
5501573Srgrimes	    break;
5511573Srgrimes	}
5521573Srgrimes	break;
5538870Srgrimes
5541573Srgrimes#ifdef HAVE_STRUCT_UTMP_UT_HOST
5551573Srgrimes    case 'm':
5561573Srgrimes	if (wp->who_host[0] == '\0')
5571573Srgrimes	    return strsave(CGETS(26, 12, "local"));
5581573Srgrimes	else {
55974963Speter	    pb = wp->who_host;
56074963Speter	    wbuf = xmalloc(strlen(pb) + 1);
5611573Srgrimes	    wb = wbuf;
5621573Srgrimes	    /* the ':' stuff is for <host>:<display>.<screen> */
5631573Srgrimes	    for (flg = isdigit((unsigned char)*pb) ? '\0' : '.';
5641573Srgrimes		 *pb != '\0' && (*pb != flg || ((pb = strchr(pb, ':')) != 0));
56574469Sjlemon		 pb++) {
5661573Srgrimes		if (*pb == ':')
5671573Srgrimes		    flg = '\0';
5681573Srgrimes		*wb++ = isupper((unsigned char)*pb) ?
5691573Srgrimes		    tolower((unsigned char)*pb) : *pb;
5701573Srgrimes	    }
5711573Srgrimes	    *wb = '\0';
5721573Srgrimes	    return wbuf;
5731573Srgrimes	}
57474963Speter
57574963Speter    case 'M':
5761573Srgrimes	if (wp->who_host[0] == '\0')
5771573Srgrimes	    return strsave(CGETS(26, 12, "local"));
5781573Srgrimes	else {
5791573Srgrimes	    pb = wp->who_host;
5801573Srgrimes	    wbuf = xmalloc(strlen(pb) + 1);
5811573Srgrimes	    wb = wbuf;
58274963Speter	    for (; *pb != '\0'; pb++)
58374963Speter		*wb++ = isupper((unsigned char)*pb) ?
58474963Speter		    tolower((unsigned char)*pb) : *pb;
5851573Srgrimes	    *wb = '\0';
58674963Speter	    return wbuf;
5871573Srgrimes	}
58874963Speter#endif /* HAVE_STRUCT_UTMP_UT_HOST */
58974963Speter
5901573Srgrimes    case 'l':
5911573Srgrimes	return strsave(wp->who_tty);
5921573Srgrimes
5931573Srgrimes    default:
5941573Srgrimes	wbuf = xmalloc(3);
59574963Speter	wbuf[0] = '%';
59674963Speter	wbuf[1] = (char) c;
5971573Srgrimes	wbuf[2] = '\0';
59874469Sjlemon	return wbuf;
5991573Srgrimes    }
60090045Sobrien    return NULL;
6011573Srgrimes}
6021573Srgrimes
6031573Srgrimesvoid
6041573Srgrimes/*ARGSUSED*/
6051573Srgrimesdolog(Char **v, struct command *c)
6061573Srgrimes{
6071573Srgrimes    struct who *wp;
6081573Srgrimes    struct varent *vp;
6091573Srgrimes
6101573Srgrimes    USE(v);
6111573Srgrimes    USE(c);
6121573Srgrimes    vp = adrof(STRwatch);	/* lint insists vp isn't used unless we */
61374963Speter    if (vp == NULL)		/* unless we assign it outside the if */
61474963Speter	stderror(ERR_NOWATCH);
6151573Srgrimes    resetwatch();
6161573Srgrimes    wp = whohead.who_next;
6178870Srgrimes    while (wp->who_next != NULL) {
6181573Srgrimes	wp->who_name[0] = '\0';
6191573Srgrimes	wp = wp->who_next;
6201573Srgrimes    }
62174921Speter}
62274918Speter
6231573Srgrimes# ifdef HAVE_STRUCT_UTMP_UT_HOST
6241573Srgrimessize_t
6251573Srgrimesutmphostsize(void)
6261573Srgrimes{
6271573Srgrimes    return UTHOSTLEN;
6281573Srgrimes}
6291573Srgrimes
6301573Srgrimeschar *
6311573Srgrimesutmphost(void)
6321573Srgrimes{
6331573Srgrimes    char *tty = short2str(varval(STRtty));
6341573Srgrimes    struct who *wp;
6351573Srgrimes    char *host = NULL;
6361573Srgrimes
6371573Srgrimes    watch_login(1);
63890045Sobrien
63990045Sobrien    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
6401573Srgrimes	if (strcmp(tty, wp->who_tty) == 0)
6411573Srgrimes	    host = wp->who_host;
6421573Srgrimes	wp->who_name[0] = '\0';
6431573Srgrimes    }
64474963Speter    resetwatch();
64574963Speter    return host;
64674963Speter}
64774963Speter# endif /* HAVE_STRUCT_UTMP_UT_HOST */
6481573Srgrimes
6491573Srgrimes#ifdef WINNT_NATIVE
6501573Srgrimesvoid add_to_who_list(name, mach_nm)
6511573Srgrimes    char *name;
65274963Speter    char *mach_nm;
65374963Speter{
6541573Srgrimes
6551573Srgrimes    struct who *wp, *wpnew;
6561573Srgrimes    int comp = -1;
6571573Srgrimes
6581573Srgrimes    wp = whohead.who_next;
6591573Srgrimes    while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0)
6601573Srgrimes	wp = wp->who_next;/* find that tty! */
6611573Srgrimes
6621573Srgrimes    if (wp->who_next && comp == 0) {	/* found the tty... */
6631573Srgrimes
6641573Srgrimes	if (*name == '\0') {
6651573Srgrimes	    wp->who_time = 0;
6661573Srgrimes	    wp->who_status = OFFLINE;
6671573Srgrimes	}
6681573Srgrimes	else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) {
6691573Srgrimes	    /* someone is logged in */
6701573Srgrimes	    wp->who_time = 0;
6711573Srgrimes	    wp->who_status = 0;	/* same guy */
6721573Srgrimes	}
6731573Srgrimes	else {
6741573Srgrimes	    (void) strncpy(wp->who_new, name, UTNAMLEN);
6751573Srgrimes	    wp->who_time = 0;
6761573Srgrimes	    if (wp->who_name[0] == '\0')
6771573Srgrimes		wp->who_status = ONLINE;
6781573Srgrimes	    else
6791573Srgrimes		wp->who_status = CHANGED;
6801573Srgrimes	}
68174469Sjlemon    }
6821573Srgrimes    else {
6831573Srgrimes	wpnew = xcalloc(1, sizeof *wpnew);
68474469Sjlemon	(void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN);
6851573Srgrimes	wpnew->who_time = 0;
68690045Sobrien	if (*name == '\0')
68790045Sobrien	    wpnew->who_status = OFFLINE;
68874918Speter	else {
6891573Srgrimes	    (void) strncpy(wpnew->who_new, name, UTNAMLEN);
6901573Srgrimes	    wpnew->who_status = ONLINE;
6911573Srgrimes	}
69280525Smikeh#ifdef WHODEBUG
69380525Smikeh	debugwholist(wpnew, wp);
69480525Smikeh#endif /* WHODEBUG */
69580525Smikeh
69674307Sjlemon	wpnew->who_next = wp;	/* link in a new 'who' */
6971573Srgrimes	wpnew->who_prev = wp->who_prev;
6988870Srgrimes	wpnew->who_prev->who_next = wpnew;
6991573Srgrimes	wp->who_prev = wpnew;	/* linked in now */
7001573Srgrimes    }
70174918Speter}
70274918Speter#endif /* WINNT_NATIVE */
70374918Speter#endif /* HAVENOUTMP */
70474918Speter