1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/tc.who.c,v 3.57 2012/01/17 20:53:38 christos Exp $ */
259243Sobrien/*
359243Sobrien * tc.who.c: Watch logins and logouts...
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35232633SmpRCSID("$tcsh: tc.who.c,v 3.57 2012/01/17 20:53:38 christos Exp $")
3659243Sobrien
3759243Sobrien#include "tc.h"
3859243Sobrien
3959243Sobrien#ifndef HAVENOUTMP
4059243Sobrien/*
4159243Sobrien * kfk 26 Jan 1984 - for login watch functions.
4259243Sobrien */
4359243Sobrien#include <ctype.h>
4459243Sobrien
45145479Smp#ifdef HAVE_UTMPX_H
4659243Sobrien# include <utmpx.h>
47232633Smp# define UTNAMLEN	sizeof(((struct utmpx *) 0)->ut_name)
48232633Smp# define UTLINLEN	sizeof(((struct utmpx *) 0)->ut_line)
49232633Smp# ifdef HAVE_STRUCT_UTMPX_UT_HOST
50232633Smp#  define UTHOSTLEN	sizeof(((struct utmpx *) 0)->ut_host)
51232633Smp# endif
5259243Sobrien/* I just redefine a few words here.  Changing every occurrence below
5359243Sobrien * seems like too much of work.  All UTMP functions have equivalent
5459243Sobrien * UTMPX counterparts, so they can be added all here when needed.
5559243Sobrien * Kimmo Suominen, Oct 14 1991
5659243Sobrien */
57145479Smp# if defined(__UTMPX_FILE) && !defined(UTMPX_FILE)
58145479Smp#  define TCSH_PATH_UTMP __UTMPX_FILE
59145479Smp# elif defined(_PATH_UTMPX)
60145479Smp#  define TCSH_PATH_UTMP _PATH_UTMPX
61145479Smp# elif defined(UTMPX_FILE)
62145479Smp#  define TCSH_PATH_UTMP UTMPX_FILE
63232633Smp# elif __FreeBSD_version >= 900000
64232633Smp#  /* Why isn't this defined somewhere? */
65232633Smp#  define TCSH_PATH_UTMP "/var/run/utx.active"
66232633Smp# elif defined(__hpux)
67232633Smp#  define TCSH_PATH_UTMP "/etc/utmpx"
68232633Smp# endif
69232633Smp# if defined(TCSH_PATH_UTMP) || !defined(HAVE_UTMP_H)
70145479Smp#  define utmp utmpx
71232633Smp#  define TCSH_USE_UTMPX
72232633Smp#  if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
73232633Smp#   define getutent getutxent
74232633Smp#   define setutent setutxent
75232633Smp#   define endutent endutxent
76232633Smp#  endif /* HAVE_GETUTENT || HAVE_GETUTXENT */
77232633Smp#  if defined(HAVE_STRUCT_UTMPX_UT_TV)
78145479Smp#   define ut_time ut_tv.tv_sec
79232633Smp#  elif defined(HAVE_STRUCT_UTMPX_UT_XTIME)
80145479Smp#   define ut_time ut_xtime
81145479Smp#  endif
82232633Smp#  if defined(HAVE_STRUCT_UTMPX_UT_USER)
83145479Smp#   define ut_name ut_user
84145479Smp#  endif
85232633Smp# endif /* TCSH_PATH_UTMP || !HAVE_UTMP_H */
86145479Smp#endif /* HAVE_UTMPX_H */
8759243Sobrien
88232633Smp#if !defined(TCSH_USE_UTMPX) && defined(HAVE_UTMP_H)
89232633Smp# include <utmp.h>
90232633Smp# if defined(HAVE_STRUCT_UTMP_UT_TV)
91232633Smp#  define ut_time ut_tv.tv_sec
92232633Smp# elif defined(HAVE_STRUCT_UTMP_UT_XTIME)
93232633Smp#  define ut_time ut_xtime
94232633Smp# endif
95232633Smp# if defined(HAVE_STRUCT_UTMP_UT_USER)
96232633Smp#  define ut_name ut_user
97232633Smp# endif
98232633Smp# ifndef BROKEN_CC
99232633Smp#  define UTNAMLEN	sizeof(((struct utmp *) 0)->ut_name)
100232633Smp#  define UTLINLEN	sizeof(((struct utmp *) 0)->ut_line)
101232633Smp#  ifdef HAVE_STRUCT_UTMP_UT_HOST
102232633Smp#   ifdef _SEQUENT_
103232633Smp#    define UTHOSTLEN	100
104232633Smp#   else
105232633Smp#    define UTHOSTLEN	sizeof(((struct utmp *) 0)->ut_host)
106232633Smp#   endif
107232633Smp#  endif	/* HAVE_STRUCT_UTMP_UT_HOST */
108232633Smp# else
10959243Sobrien/* give poor cc a little help if it needs it */
11059243Sobrienstruct utmp __ut;
111232633Smp#  define UTNAMLEN	sizeof(__ut.ut_name)
112232633Smp#  define UTLINLEN	sizeof(__ut.ut_line)
113232633Smp#  ifdef HAVE_STRUCT_UTMP_UT_HOST
114232633Smp#   ifdef _SEQUENT_
115232633Smp#    define UTHOSTLEN	100
116232633Smp#   else
117232633Smp#    define UTHOSTLEN	sizeof(__ut.ut_host)
118232633Smp#   endif
119232633Smp#  endif /* HAVE_STRUCT_UTMP_UT_HOST */
120232633Smp# endif /* BROKEN_CC */
121232633Smp# ifndef TCSH_PATH_UTMP
122232633Smp#  ifdef UTMP_FILE
123232633Smp#   define TCSH_PATH_UTMP UTMP_FILE
124232633Smp#  elif defined(_PATH_UTMP)
125232633Smp#   define TCSH_PATH_UTMP _PATH_UTMP
12659243Sobrien#  else
127232633Smp#   define TCSH_PATH_UTMP "/etc/utmp"
128232633Smp#  endif /* UTMP_FILE */
129232633Smp# endif /* TCSH_PATH_UTMP */
130232633Smp#endif /* !TCSH_USE_UTMPX && HAVE_UTMP_H */
13159243Sobrien
132232633Smp#ifndef UTNAMLEN
133232633Smp#define UTNAMLEN 64
134232633Smp#endif
135232633Smp#ifndef UTLINLEN
136232633Smp#define UTLINLEN 64
137232633Smp#endif
13859243Sobrien
13959243Sobrienstruct who {
14059243Sobrien    struct who *who_next;
14159243Sobrien    struct who *who_prev;
14259243Sobrien    char    who_name[UTNAMLEN + 1];
14359243Sobrien    char    who_new[UTNAMLEN + 1];
14459243Sobrien    char    who_tty[UTLINLEN + 1];
145232633Smp#ifdef UTHOSTLEN
14659243Sobrien    char    who_host[UTHOSTLEN + 1];
147232633Smp#endif /* UTHOSTLEN */
14859243Sobrien    time_t  who_time;
14959243Sobrien    int     who_status;
15059243Sobrien};
15159243Sobrien
15259243Sobrienstatic struct who whohead, whotail;
15359243Sobrienstatic time_t watch_period = 0;
15459243Sobrienstatic time_t stlast = 0;
15559243Sobrien#ifdef WHODEBUG
156167465Smpstatic	void	debugwholist	(struct who *, struct who *);
15759243Sobrien#endif
158167465Smpstatic	void	print_who	(struct who *);
15959243Sobrien
16059243Sobrien
16159243Sobrien#define ONLINE		01
16259243Sobrien#define OFFLINE		02
16359243Sobrien#define CHANGED		04
16459243Sobrien#define STMASK		07
16559243Sobrien#define ANNOUNCE	010
166167465Smp#define CLEARED		020
16759243Sobrien
16859243Sobrien/*
16959243Sobrien * Karl Kleinpaste, 26 Jan 1984.
17059243Sobrien * Initialize the dummy tty list for login watch.
17159243Sobrien * This dummy list eliminates boundary conditions
17259243Sobrien * when doing pointer-chase searches.
17359243Sobrien */
17459243Sobrienvoid
175167465Smpinitwatch(void)
17659243Sobrien{
17759243Sobrien    whohead.who_next = &whotail;
17859243Sobrien    whotail.who_prev = &whohead;
17959243Sobrien    stlast = 1;
18059243Sobrien#ifdef WHODEBUG
18159243Sobrien    debugwholist(NULL, NULL);
18259243Sobrien#endif /* WHODEBUG */
18359243Sobrien}
18459243Sobrien
18559243Sobrienvoid
186167465Smpresetwatch(void)
18759243Sobrien{
18859243Sobrien    watch_period = 0;
18959243Sobrien    stlast = 0;
19059243Sobrien}
19159243Sobrien
19259243Sobrien/*
19359243Sobrien * Karl Kleinpaste, 26 Jan 1984.
19459243Sobrien * Watch /etc/utmp for login/logout changes.
19559243Sobrien */
19659243Sobrienvoid
197167465Smpwatch_login(int force)
19859243Sobrien{
199145479Smp    int     comp = -1, alldone;
20059243Sobrien    int	    firsttime = stlast == 1;
201232633Smp#if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
202145479Smp    struct utmp *uptr;
203145479Smp#else
204145479Smp    int utmpfd;
205145479Smp#endif
20659243Sobrien    struct utmp utmp;
20759243Sobrien    struct who *wp, *wpnew;
20859243Sobrien    struct varent *v;
20959243Sobrien    Char  **vp = NULL;
21059243Sobrien    time_t  t, interval = MAILINTVL;
21159243Sobrien    struct stat sta;
212145479Smp#if defined(HAVE_STRUCT_UTMP_UT_HOST) && defined(_SEQUENT_)
21359243Sobrien    char   *host, *ut_find_host();
21459243Sobrien#endif
21569408Sache#ifdef WINNT_NATIVE
21659243Sobrien    static int ncbs_posted = 0;
21759243Sobrien    USE(utmp);
21859243Sobrien    USE(utmpfd);
21959243Sobrien    USE(sta);
22059243Sobrien    USE(wpnew);
22169408Sache#endif /* WINNT_NATIVE */
22259243Sobrien
22359243Sobrien    /* stop SIGINT, lest our login list get trashed. */
224167465Smp    pintr_disabled++;
225167465Smp    cleanup_push(&pintr_disabled, disabled_cleanup);
22659243Sobrien
22759243Sobrien    v = adrof(STRwatch);
228100616Smp    if ((v == NULL || v->vec == NULL) && !force) {
229167465Smp	cleanup_until(&pintr_disabled);
23059243Sobrien	return;			/* no names to watch */
23159243Sobrien    }
23259243Sobrien    if (!force) {
23359243Sobrien	trim(vp = v->vec);
23459243Sobrien	if (blklen(vp) % 2)		/* odd # args: 1st == # minutes. */
23559243Sobrien	    interval = (number(*vp)) ? (getn(*vp++) * 60) : MAILINTVL;
23659243Sobrien    }
23759243Sobrien    else
23859243Sobrien	interval = 0;
23959243Sobrien
24059243Sobrien    (void) time(&t);
24169408Sache#ifdef WINNT_NATIVE
24259243Sobrien	/*
24359243Sobrien	 * Since NCB_ASTATs take time, start em async at least 90 secs
24459243Sobrien	 * before we are due -amol 6/5/97
24559243Sobrien	 */
24659243Sobrien	if (!ncbs_posted) {
247167465Smp	    time_t tdiff = t - watch_period;
24859243Sobrien	    if (!watch_period || ((tdiff  > 0) && (tdiff > (interval - 90)))) {
24959243Sobrien		start_ncbs(vp);
25059243Sobrien 		ncbs_posted = 1;
25159243Sobrien	    }
25259243Sobrien	}
25369408Sache#endif /* WINNT_NATIVE */
25459243Sobrien    if (t - watch_period < interval) {
255167465Smp	cleanup_until(&pintr_disabled);
25659243Sobrien	return;			/* not long enough yet... */
25759243Sobrien    }
25859243Sobrien    watch_period = t;
25969408Sache#ifdef WINNT_NATIVE
26059243Sobrien    ncbs_posted = 0;
26169408Sache#else /* !WINNT_NATIVE */
26259243Sobrien
26359243Sobrien    /*
26459243Sobrien     * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
26559243Sobrien     * Don't open utmp all the time, stat it first...
26659243Sobrien     */
267145479Smp    if (stat(TCSH_PATH_UTMP, &sta)) {
26861515Sobrien	if (!force)
26961515Sobrien	    xprintf(CGETS(26, 1,
27061515Sobrien			  "cannot stat %s.  Please \"unset watch\".\n"),
271145479Smp		    TCSH_PATH_UTMP);
272167465Smp	cleanup_until(&pintr_disabled);
27359243Sobrien	return;
27459243Sobrien    }
27559243Sobrien    if (stlast == sta.st_mtime) {
276167465Smp	cleanup_until(&pintr_disabled);
27759243Sobrien	return;
27859243Sobrien    }
27959243Sobrien    stlast = sta.st_mtime;
280232633Smp#if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
281145479Smp    setutent();
282145479Smp#else
283167465Smp    if ((utmpfd = xopen(TCSH_PATH_UTMP, O_RDONLY|O_LARGEFILE)) < 0) {
28461515Sobrien	if (!force)
28561515Sobrien	    xprintf(CGETS(26, 2,
28661515Sobrien			  "%s cannot be opened.  Please \"unset watch\".\n"),
287145479Smp		    TCSH_PATH_UTMP);
288167465Smp	cleanup_until(&pintr_disabled);
28959243Sobrien	return;
29059243Sobrien    }
291167465Smp    cleanup_push(&utmpfd, open_cleanup);
292145479Smp#endif
29359243Sobrien
29459243Sobrien    /*
29559243Sobrien     * xterm clears the entire utmp entry - mark everyone on the status list
29659243Sobrien     * OFFLINE or we won't notice X "logouts"
29759243Sobrien     */
298167465Smp    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next)
299167465Smp	wp->who_status = OFFLINE | CLEARED;
30059243Sobrien
30159243Sobrien    /*
30259243Sobrien     * Read in the utmp file, sort the entries, and update existing entries or
30359243Sobrien     * add new entries to the status list.
30459243Sobrien     */
305232633Smp#if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
306145479Smp    while ((uptr = getutent()) != NULL) {
307145479Smp        memcpy(&utmp, uptr, sizeof (utmp));
308145479Smp#else
309167465Smp    while (xread(utmpfd, &utmp, sizeof utmp) == sizeof utmp) {
310145479Smp#endif
31159243Sobrien
31259243Sobrien# ifdef DEAD_PROCESS
31359243Sobrien#  ifndef IRIS4D
31459243Sobrien	if (utmp.ut_type != USER_PROCESS)
31559243Sobrien	    continue;
31659243Sobrien#  else
31759243Sobrien	/* Why is that? Cause the utmp file is always corrupted??? */
31859243Sobrien	if (utmp.ut_type != USER_PROCESS && utmp.ut_type != DEAD_PROCESS)
31959243Sobrien	    continue;
32059243Sobrien#  endif /* IRIS4D */
32159243Sobrien# endif /* DEAD_PROCESS */
32259243Sobrien
32359243Sobrien	if (utmp.ut_name[0] == '\0' && utmp.ut_line[0] == '\0')
32459243Sobrien	    continue;	/* completely void entry */
32559243Sobrien# ifdef DEAD_PROCESS
32659243Sobrien	if (utmp.ut_type == DEAD_PROCESS && utmp.ut_line[0] == '\0')
32759243Sobrien	    continue;
32859243Sobrien# endif /* DEAD_PROCESS */
32959243Sobrien	wp = whohead.who_next;
33059243Sobrien	while (wp->who_next && (comp = strncmp(wp->who_tty, utmp.ut_line, UTLINLEN)) < 0)
33159243Sobrien	    wp = wp->who_next;/* find that tty! */
33259243Sobrien
33359243Sobrien	if (wp->who_next && comp == 0) {	/* found the tty... */
334167465Smp	    if (utmp.ut_time < wp->who_time)
335167465Smp	        continue;
33659243Sobrien# ifdef DEAD_PROCESS
33759243Sobrien	    if (utmp.ut_type == DEAD_PROCESS) {
33859243Sobrien		wp->who_time = utmp.ut_time;
33959243Sobrien		wp->who_status = OFFLINE;
34059243Sobrien	    }
34159243Sobrien	    else
34259243Sobrien# endif /* DEAD_PROCESS */
34359243Sobrien	    if (utmp.ut_name[0] == '\0') {
34459243Sobrien		wp->who_time = utmp.ut_time;
34559243Sobrien		wp->who_status = OFFLINE;
34659243Sobrien	    }
34759243Sobrien	    else if (strncmp(utmp.ut_name, wp->who_name, UTNAMLEN) == 0) {
34859243Sobrien		/* someone is logged in */
34959243Sobrien		wp->who_time = utmp.ut_time;
350167465Smp		wp->who_status = ONLINE | ANNOUNCE;	/* same guy */
35159243Sobrien	    }
35259243Sobrien	    else {
35359243Sobrien		(void) strncpy(wp->who_new, utmp.ut_name, UTNAMLEN);
354232633Smp# ifdef UTHOSTLEN
35559243Sobrien#  ifdef _SEQUENT_
35659243Sobrien		host = ut_find_host(wp->who_tty);
35759243Sobrien		if (host)
35859243Sobrien		    (void) strncpy(wp->who_host, host, UTHOSTLEN);
35959243Sobrien		else
36059243Sobrien		    wp->who_host[0] = 0;
36159243Sobrien#  else
36259243Sobrien		(void) strncpy(wp->who_host, utmp.ut_host, UTHOSTLEN);
36359243Sobrien#  endif
364232633Smp# endif /* UTHOSTLEN */
36559243Sobrien		wp->who_time = utmp.ut_time;
36659243Sobrien		if (wp->who_name[0] == '\0')
36759243Sobrien		    wp->who_status = ONLINE;
36859243Sobrien		else
36959243Sobrien		    wp->who_status = CHANGED;
37059243Sobrien	    }
37159243Sobrien	}
37259243Sobrien	else {		/* new tty in utmp */
373167465Smp	    wpnew = xcalloc(1, sizeof *wpnew);
37459243Sobrien	    (void) strncpy(wpnew->who_tty, utmp.ut_line, UTLINLEN);
375232633Smp# ifdef UTHOSTLEN
37659243Sobrien#  ifdef _SEQUENT_
37759243Sobrien	    host = ut_find_host(wpnew->who_tty);
37859243Sobrien	    if (host)
37959243Sobrien		(void) strncpy(wpnew->who_host, host, UTHOSTLEN);
38059243Sobrien	    else
38159243Sobrien		wpnew->who_host[0] = 0;
38259243Sobrien#  else
38359243Sobrien	    (void) strncpy(wpnew->who_host, utmp.ut_host, UTHOSTLEN);
38459243Sobrien#  endif
385232633Smp# endif /* UTHOSTLEN */
38659243Sobrien	    wpnew->who_time = utmp.ut_time;
38759243Sobrien# ifdef DEAD_PROCESS
38859243Sobrien	    if (utmp.ut_type == DEAD_PROCESS)
38959243Sobrien		wpnew->who_status = OFFLINE;
39059243Sobrien	    else
39159243Sobrien# endif /* DEAD_PROCESS */
39259243Sobrien	    if (utmp.ut_name[0] == '\0')
39359243Sobrien		wpnew->who_status = OFFLINE;
39459243Sobrien	    else {
39559243Sobrien		(void) strncpy(wpnew->who_new, utmp.ut_name, UTNAMLEN);
39659243Sobrien		wpnew->who_status = ONLINE;
39759243Sobrien	    }
39859243Sobrien# ifdef WHODEBUG
39959243Sobrien	    debugwholist(wpnew, wp);
40059243Sobrien# endif /* WHODEBUG */
40159243Sobrien
40259243Sobrien	    wpnew->who_next = wp;	/* link in a new 'who' */
40359243Sobrien	    wpnew->who_prev = wp->who_prev;
40459243Sobrien	    wpnew->who_prev->who_next = wpnew;
40559243Sobrien	    wp->who_prev = wpnew;	/* linked in now */
40659243Sobrien	}
40759243Sobrien    }
408232633Smp#if defined(HAVE_GETUTENT) || defined(HAVE_GETUTXENT)
409145479Smp    endutent();
410145479Smp#else
411167465Smp    cleanup_until(&utmpfd);
412145479Smp#endif
41369408Sache#endif /* !WINNT_NATIVE */
41459243Sobrien
415167465Smp    if (force || vp == NULL) {
416167465Smp	cleanup_until(&pintr_disabled);
41759243Sobrien	return;
418167465Smp    }
41959243Sobrien
42059243Sobrien    /*
42159243Sobrien     * The state of all logins is now known, so we can search the user's list
42259243Sobrien     * of watchables to print the interesting ones.
42359243Sobrien     */
42459243Sobrien    for (alldone = 0; !alldone && *vp != NULL && **vp != '\0' &&
42559243Sobrien	 *(vp + 1) != NULL && **(vp + 1) != '\0';
42659243Sobrien	 vp += 2) {		/* args used in pairs... */
42759243Sobrien
42859243Sobrien	if (eq(*vp, STRany) && eq(*(vp + 1), STRany))
42959243Sobrien	    alldone = 1;
43059243Sobrien
43159243Sobrien	for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
43259243Sobrien	    if (wp->who_status & ANNOUNCE ||
43359243Sobrien		(!eq(STRany, vp[0]) &&
43459243Sobrien		 !Gmatch(str2short(wp->who_name), vp[0]) &&
43559243Sobrien		 !Gmatch(str2short(wp->who_new),  vp[0])) ||
43659243Sobrien		(!Gmatch(str2short(wp->who_tty),  vp[1]) &&
43759243Sobrien		 !eq(STRany, vp[1])))
43859243Sobrien		continue;	/* entry doesn't qualify */
43959243Sobrien	    /* already printed or not right one to print */
44059243Sobrien
44159243Sobrien
442167465Smp	    if (wp->who_status & CLEARED) {/* utmp entry was cleared */
44359243Sobrien		wp->who_time = watch_period;
444167465Smp		wp->who_status &= ~CLEARED;
445167465Smp	    }
44659243Sobrien
44759243Sobrien	    if ((wp->who_status & OFFLINE) &&
44859243Sobrien		(wp->who_name[0] != '\0')) {
44959243Sobrien		if (!firsttime)
45059243Sobrien		    print_who(wp);
45159243Sobrien		wp->who_name[0] = '\0';
45259243Sobrien		wp->who_status |= ANNOUNCE;
45359243Sobrien		continue;
45459243Sobrien	    }
45559243Sobrien	    if (wp->who_status & ONLINE) {
45659243Sobrien		if (!firsttime)
45759243Sobrien		    print_who(wp);
45859243Sobrien		(void) strcpy(wp->who_name, wp->who_new);
45959243Sobrien		wp->who_status |= ANNOUNCE;
46059243Sobrien		continue;
46159243Sobrien	    }
46259243Sobrien	    if (wp->who_status & CHANGED) {
46359243Sobrien		if (!firsttime)
46459243Sobrien		    print_who(wp);
46559243Sobrien		(void) strcpy(wp->who_name, wp->who_new);
46659243Sobrien		wp->who_status |= ANNOUNCE;
46759243Sobrien		continue;
46859243Sobrien	    }
46959243Sobrien	}
47059243Sobrien    }
471167465Smp    cleanup_until(&pintr_disabled);
47259243Sobrien}
47359243Sobrien
47459243Sobrien#ifdef WHODEBUG
47559243Sobrienstatic void
476167465Smpdebugwholist(struct who *new, struct who *wp)
47759243Sobrien{
478145479Smp    struct who *a;
47959243Sobrien
48059243Sobrien    a = whohead.who_next;
48159243Sobrien    while (a->who_next != NULL) {
48259243Sobrien	xprintf("%s/%s -> ", a->who_name, a->who_tty);
48359243Sobrien	a = a->who_next;
48459243Sobrien    }
48559243Sobrien    xprintf("TAIL\n");
48659243Sobrien    if (a != &whotail) {
48759243Sobrien	xprintf(CGETS(26, 3, "BUG! last element is not whotail!\n"));
48859243Sobrien	abort();
48959243Sobrien    }
49059243Sobrien    a = whotail.who_prev;
49159243Sobrien    xprintf(CGETS(26, 4, "backward: "));
49259243Sobrien    while (a->who_prev != NULL) {
49359243Sobrien	xprintf("%s/%s -> ", a->who_name, a->who_tty);
49459243Sobrien	a = a->who_prev;
49559243Sobrien    }
49659243Sobrien    xprintf("HEAD\n");
49759243Sobrien    if (a != &whohead) {
49859243Sobrien	xprintf(CGETS(26, 5, "BUG! first element is not whohead!\n"));
49959243Sobrien	abort();
50059243Sobrien    }
50159243Sobrien    if (new)
50259243Sobrien	xprintf(CGETS(26, 6, "new: %s/%s\n"), new->who_name, new->who_tty);
50359243Sobrien    if (wp)
50459243Sobrien	xprintf("wp: %s/%s\n", wp->who_name, wp->who_tty);
50559243Sobrien}
50659243Sobrien#endif /* WHODEBUG */
50759243Sobrien
50859243Sobrien
50959243Sobrienstatic void
510167465Smpprint_who(struct who *wp)
51159243Sobrien{
512232633Smp#ifdef UTHOSTLEN
51359243Sobrien    Char   *cp = str2short(CGETS(26, 7, "%n has %a %l from %m."));
51459243Sobrien#else
51559243Sobrien    Char   *cp = str2short(CGETS(26, 8, "%n has %a %l."));
516232633Smp#endif /* UTHOSTLEN */
51759243Sobrien    struct varent *vp = adrof(STRwho);
518167465Smp    Char *str;
51959243Sobrien
520100616Smp    if (vp && vp->vec && vp->vec[0])
52159243Sobrien	cp = vp->vec[0];
52259243Sobrien
523167465Smp    str = tprintf(FMT_WHO, cp, NULL, wp->who_time, wp);
524167465Smp    cleanup_push(str, xfree);
525167465Smp    for (cp = str; *cp;)
526145479Smp	xputwchar(*cp++);
527167465Smp    cleanup_until(str);
52859243Sobrien    xputchar('\n');
52959243Sobrien} /* end print_who */
53059243Sobrien
53159243Sobrien
532167465Smpchar *
533167465Smpwho_info(ptr_t ptr, int c)
534167465Smp{
535167465Smp    struct who *wp = ptr;
53659243Sobrien    char *wbuf;
537232633Smp#ifdef UTHOSTLEN
538167465Smp    char *wb;
53959243Sobrien    int flg;
54059243Sobrien    char *pb;
541232633Smp#endif /* UTHOSTLEN */
54259243Sobrien
54359243Sobrien    switch (c) {
54459243Sobrien    case 'n':		/* user name */
54559243Sobrien	switch (wp->who_status & STMASK) {
54659243Sobrien	case ONLINE:
54759243Sobrien	case CHANGED:
548167465Smp	    return strsave(wp->who_new);
54959243Sobrien	case OFFLINE:
550167465Smp	    return strsave(wp->who_name);
55159243Sobrien	default:
55259243Sobrien	    break;
55359243Sobrien	}
55459243Sobrien	break;
55559243Sobrien
55659243Sobrien    case 'a':
55759243Sobrien	switch (wp->who_status & STMASK) {
55859243Sobrien	case ONLINE:
559167465Smp	    return strsave(CGETS(26, 9, "logged on"));
56059243Sobrien	case OFFLINE:
561167465Smp	    return strsave(CGETS(26, 10, "logged off"));
56259243Sobrien	case CHANGED:
563167465Smp	    return xasprintf(CGETS(26, 11, "replaced %s on"), wp->who_name);
56459243Sobrien	default:
56559243Sobrien	    break;
56659243Sobrien	}
56759243Sobrien	break;
56859243Sobrien
569232633Smp#ifdef UTHOSTLEN
57059243Sobrien    case 'm':
57159243Sobrien	if (wp->who_host[0] == '\0')
572167465Smp	    return strsave(CGETS(26, 12, "local"));
57359243Sobrien	else {
574167465Smp	    pb = wp->who_host;
575167465Smp	    wbuf = xmalloc(strlen(pb) + 1);
576167465Smp	    wb = wbuf;
57759243Sobrien	    /* the ':' stuff is for <host>:<display>.<screen> */
578167465Smp	    for (flg = isdigit((unsigned char)*pb) ? '\0' : '.';
579167465Smp		 *pb != '\0' && (*pb != flg || ((pb = strchr(pb, ':')) != 0));
58059243Sobrien		 pb++) {
58159243Sobrien		if (*pb == ':')
58259243Sobrien		    flg = '\0';
583145479Smp		*wb++ = isupper((unsigned char)*pb) ?
584145479Smp		    tolower((unsigned char)*pb) : *pb;
58559243Sobrien	    }
58659243Sobrien	    *wb = '\0';
58759243Sobrien	    return wbuf;
58859243Sobrien	}
58959243Sobrien
59059243Sobrien    case 'M':
59159243Sobrien	if (wp->who_host[0] == '\0')
592167465Smp	    return strsave(CGETS(26, 12, "local"));
59359243Sobrien	else {
594167465Smp	    pb = wp->who_host;
595167465Smp	    wbuf = xmalloc(strlen(pb) + 1);
596167465Smp	    wb = wbuf;
597167465Smp	    for (; *pb != '\0'; pb++)
598145479Smp		*wb++ = isupper((unsigned char)*pb) ?
599145479Smp		    tolower((unsigned char)*pb) : *pb;
60059243Sobrien	    *wb = '\0';
60159243Sobrien	    return wbuf;
60259243Sobrien	}
603232633Smp#endif /* UTHOSTLEN */
60459243Sobrien
60559243Sobrien    case 'l':
606167465Smp	return strsave(wp->who_tty);
60759243Sobrien
60859243Sobrien    default:
609167465Smp	wbuf = xmalloc(3);
61059243Sobrien	wbuf[0] = '%';
61159243Sobrien	wbuf[1] = (char) c;
61259243Sobrien	wbuf[2] = '\0';
61359243Sobrien	return wbuf;
61459243Sobrien    }
61559243Sobrien    return NULL;
61659243Sobrien}
61759243Sobrien
61859243Sobrienvoid
61959243Sobrien/*ARGSUSED*/
620167465Smpdolog(Char **v, struct command *c)
62159243Sobrien{
62259243Sobrien    struct who *wp;
62359243Sobrien    struct varent *vp;
62459243Sobrien
62559243Sobrien    USE(v);
62659243Sobrien    USE(c);
62759243Sobrien    vp = adrof(STRwatch);	/* lint insists vp isn't used unless we */
62859243Sobrien    if (vp == NULL)		/* unless we assign it outside the if */
62959243Sobrien	stderror(ERR_NOWATCH);
63059243Sobrien    resetwatch();
63159243Sobrien    wp = whohead.who_next;
63259243Sobrien    while (wp->who_next != NULL) {
63359243Sobrien	wp->who_name[0] = '\0';
63459243Sobrien	wp = wp->who_next;
63559243Sobrien    }
63659243Sobrien}
63759243Sobrien
638232633Smp# ifdef UTHOSTLEN
63969408Sachesize_t
640167465Smputmphostsize(void)
64169408Sache{
64269408Sache    return UTHOSTLEN;
64369408Sache}
64469408Sache
64559243Sobrienchar *
646167465Smputmphost(void)
64759243Sobrien{
64859243Sobrien    char *tty = short2str(varval(STRtty));
64959243Sobrien    struct who *wp;
65059243Sobrien    char *host = NULL;
65159243Sobrien
65259243Sobrien    watch_login(1);
65359243Sobrien
65459243Sobrien    for (wp = whohead.who_next; wp->who_next != NULL; wp = wp->who_next) {
65559243Sobrien	if (strcmp(tty, wp->who_tty) == 0)
65659243Sobrien	    host = wp->who_host;
65759243Sobrien	wp->who_name[0] = '\0';
65859243Sobrien    }
65959243Sobrien    resetwatch();
66059243Sobrien    return host;
66159243Sobrien}
662232633Smp# endif /* UTHOSTLEN */
66359243Sobrien
66469408Sache#ifdef WINNT_NATIVE
665232633Smpvoid
666232633Smpadd_to_who_list(char *name, char *mach_nm)
66759243Sobrien{
66859243Sobrien
66959243Sobrien    struct who *wp, *wpnew;
67059243Sobrien    int comp = -1;
67159243Sobrien
67259243Sobrien    wp = whohead.who_next;
67359243Sobrien    while (wp->who_next && (comp = strncmp(wp->who_tty,mach_nm,UTLINLEN)) < 0)
67459243Sobrien	wp = wp->who_next;/* find that tty! */
67559243Sobrien
67659243Sobrien    if (wp->who_next && comp == 0) {	/* found the tty... */
67759243Sobrien
67859243Sobrien	if (*name == '\0') {
67959243Sobrien	    wp->who_time = 0;
68059243Sobrien	    wp->who_status = OFFLINE;
68159243Sobrien	}
68259243Sobrien	else if (strncmp(name, wp->who_name, UTNAMLEN) == 0) {
68359243Sobrien	    /* someone is logged in */
68459243Sobrien	    wp->who_time = 0;
68559243Sobrien	    wp->who_status = 0;	/* same guy */
68659243Sobrien	}
68759243Sobrien	else {
68859243Sobrien	    (void) strncpy(wp->who_new, name, UTNAMLEN);
68959243Sobrien	    wp->who_time = 0;
69059243Sobrien	    if (wp->who_name[0] == '\0')
69159243Sobrien		wp->who_status = ONLINE;
69259243Sobrien	    else
69359243Sobrien		wp->who_status = CHANGED;
69459243Sobrien	}
69559243Sobrien    }
69659243Sobrien    else {
697167465Smp	wpnew = xcalloc(1, sizeof *wpnew);
69859243Sobrien	(void) strncpy(wpnew->who_tty, mach_nm, UTLINLEN);
69959243Sobrien	wpnew->who_time = 0;
70059243Sobrien	if (*name == '\0')
70159243Sobrien	    wpnew->who_status = OFFLINE;
70259243Sobrien	else {
70359243Sobrien	    (void) strncpy(wpnew->who_new, name, UTNAMLEN);
70459243Sobrien	    wpnew->who_status = ONLINE;
70559243Sobrien	}
70659243Sobrien#ifdef WHODEBUG
70759243Sobrien	debugwholist(wpnew, wp);
70859243Sobrien#endif /* WHODEBUG */
70959243Sobrien
71059243Sobrien	wpnew->who_next = wp;	/* link in a new 'who' */
71159243Sobrien	wpnew->who_prev = wp->who_prev;
71259243Sobrien	wpnew->who_prev->who_next = wpnew;
71359243Sobrien	wp->who_prev = wpnew;	/* linked in now */
71459243Sobrien    }
71559243Sobrien}
71669408Sache#endif /* WINNT_NATIVE */
71759243Sobrien#endif /* HAVENOUTMP */
718