wall.c revision 76367
11590Srgrimes/*
21590Srgrimes * Copyright (c) 1988, 1990, 1993
31590Srgrimes *	The Regents of the University of California.  All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. All advertising materials mentioning features or use of this software
141590Srgrimes *    must display the following acknowledgement:
151590Srgrimes *	This product includes software developed by the University of
161590Srgrimes *	California, Berkeley and its contributors.
171590Srgrimes * 4. Neither the name of the University nor the names of its contributors
181590Srgrimes *    may be used to endorse or promote products derived from this software
191590Srgrimes *    without specific prior written permission.
201590Srgrimes *
211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311590Srgrimes * SUCH DAMAGE.
321590Srgrimes */
331590Srgrimes
341590Srgrimes#ifndef lint
3528695Scharnierstatic const char copyright[] =
361590Srgrimes"@(#) Copyright (c) 1988, 1990, 1993\n\
371590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381590Srgrimes#endif /* not lint */
391590Srgrimes
401590Srgrimes#ifndef lint
4128695Scharnier#if 0
421590Srgrimesstatic char sccsid[] = "@(#)wall.c	8.2 (Berkeley) 11/16/93";
4328695Scharnier#endif
4428695Scharnierstatic const char rcsid[] =
4550477Speter  "$FreeBSD: head/usr.bin/wall/wall.c 76367 2001-05-08 11:11:42Z kris $";
461590Srgrimes#endif /* not lint */
471590Srgrimes
481590Srgrimes/*
491590Srgrimes * This program is not related to David Wall, whose Stanford Ph.D. thesis
501590Srgrimes * is entitled "Mechanisms for Broadcast and Selective Broadcast".
511590Srgrimes */
521590Srgrimes
531590Srgrimes#include <sys/param.h>
541590Srgrimes#include <sys/stat.h>
551590Srgrimes#include <sys/uio.h>
561590Srgrimes
5728695Scharnier#include <ctype.h>
5828695Scharnier#include <err.h>
5973255Simp#include <grp.h>
6025777Sache#include <locale.h>
611590Srgrimes#include <paths.h>
621590Srgrimes#include <pwd.h>
631590Srgrimes#include <stdio.h>
641590Srgrimes#include <stdlib.h>
651590Srgrimes#include <string.h>
6629434Sache#include <time.h>
671590Srgrimes#include <unistd.h>
681590Srgrimes#include <utmp.h>
691590Srgrimes
7073255Simpstatic void makemsg(char *);
7173255Simpstatic void usage(void);
7273255Simpchar   *ttymsg(struct iovec *, int, const char *, int);
731590Srgrimes
741590Srgrimes#define	IGNOREUSER	"sleeper"
751590Srgrimes
7673255Simpstruct wallgroup {
7773255Simp	struct wallgroup *next;
7873255Simp	char		*name;
7973255Simp	gid_t		gid;
8073255Simp} *grouplist;
811590Srgrimesint nobanner;
821590Srgrimesint mbufsize;
831590Srgrimeschar *mbuf;
841590Srgrimes
851590Srgrimesint
8673255Simpmain(int argc, char *argv[])
871590Srgrimes{
881590Srgrimes	struct iovec iov;
891590Srgrimes	struct utmp utmp;
9073255Simp	gid_t grps[NGROUPS_MAX];
9173255Simp	int ch;
9273255Simp	int ingroup, ngrps, i;
931590Srgrimes	FILE *fp;
9473255Simp	struct wallgroup *g;
9573255Simp	struct group *grp;
9669231Skris	char *p;
9773255Simp	struct passwd *pw;
981590Srgrimes	char line[sizeof(utmp.ut_line) + 1];
9973255Simp	char username[sizeof(utmp.ut_name) + 1];
1001590Srgrimes
10173255Simp	ingroup = 0;
10225777Sache	(void)setlocale(LC_CTYPE, "");
10325777Sache
10473255Simp	while ((ch = getopt(argc, argv, "g:n")) != -1)
1051590Srgrimes		switch (ch) {
1061590Srgrimes		case 'n':
1071590Srgrimes			/* undoc option for shutdown: suppress banner */
1081590Srgrimes			if (geteuid() == 0)
1091590Srgrimes				nobanner = 1;
1101590Srgrimes			break;
11173255Simp		case 'g':
11273255Simp			g = (struct wallgroup *)malloc(sizeof *g);
11373255Simp			g->next = grouplist;
11473255Simp			g->name = optarg;
11573255Simp			g->gid = -1;
11673255Simp			grouplist = g;
11773255Simp			break;
1181590Srgrimes		case '?':
1191590Srgrimes		default:
12028695Scharnier			usage();
1211590Srgrimes		}
1221590Srgrimes	argc -= optind;
1231590Srgrimes	argv += optind;
1241590Srgrimes	if (argc > 1)
12528695Scharnier		usage();
1261590Srgrimes
12773255Simp	for (g = grouplist; g; g = g->next) {
12873255Simp		grp = getgrnam(g->name);
12973320Simp		if (grp != NULL)
13073255Simp			g->gid = grp->gr_gid;
13173320Simp		else
13273320Simp			warnx("%s: no such group", g->name);
13373255Simp	}
13473255Simp
1351590Srgrimes	makemsg(*argv);
1361590Srgrimes
13728695Scharnier	if (!(fp = fopen(_PATH_UTMP, "r")))
13869231Skris		err(1, "cannot read %s", _PATH_UTMP);
1391590Srgrimes	iov.iov_base = mbuf;
1401590Srgrimes	iov.iov_len = mbufsize;
1411590Srgrimes	/* NOSTRICT */
1421590Srgrimes	while (fread((char *)&utmp, sizeof(utmp), 1, fp) == 1) {
1431590Srgrimes		if (!utmp.ut_name[0] ||
1441590Srgrimes		    !strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name)))
1451590Srgrimes			continue;
14673255Simp		if (grouplist) {
14773255Simp			strlcpy(username, utmp.ut_name, sizeof(utmp.ut_name));
14873255Simp			pw = getpwnam(username);
14973255Simp			if (!pw)
15073255Simp				continue;
15173255Simp			ngrps = getgroups(pw->pw_gid, grps);
15273255Simp			for (g = grouplist; g && ingroup == 0; g = g->next) {
15373255Simp				if (g->gid == -1)
15473255Simp					continue;
15573255Simp				if (g->gid == pw->pw_gid)
15673255Simp					ingroup = 1;
15773255Simp				for (i = 0; i < ngrps && ingroup == 0; i++)
15873255Simp					if (g->gid == grps[i])
15973255Simp						ingroup = 1;
16073255Simp			}
16173255Simp			if (ingroup == 0)
16273255Simp				continue;
16373255Simp		}
1641590Srgrimes		strncpy(line, utmp.ut_line, sizeof(utmp.ut_line));
1651590Srgrimes		line[sizeof(utmp.ut_line)] = '\0';
1661590Srgrimes		if ((p = ttymsg(&iov, 1, line, 60*5)) != NULL)
16728695Scharnier			warnx("%s", p);
1681590Srgrimes	}
1691590Srgrimes	exit(0);
1701590Srgrimes}
1711590Srgrimes
17228695Scharnierstatic void
17328695Scharnierusage()
17428695Scharnier{
17573320Simp	(void)fprintf(stderr, "usage: wall [-g group] [file]\n");
17628695Scharnier	exit(1);
17728695Scharnier}
17828695Scharnier
1791590Srgrimesvoid
18073255Simpmakemsg(char *fname)
1811590Srgrimes{
18273255Simp	int cnt;
18373255Simp	unsigned char ch;
1841590Srgrimes	struct tm *lt;
1851590Srgrimes	struct passwd *pw;
1861590Srgrimes	struct stat sbuf;
18729434Sache	time_t now;
1881590Srgrimes	FILE *fp;
1891590Srgrimes	int fd;
19069231Skris	char *p, *tty, hostname[MAXHOSTNAMELEN], lbuf[256], tmpname[64];
19169231Skris	const char *whom;
19276367Skris	gid_t egid;
1931590Srgrimes
19469231Skris	(void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP);
19569231Skris	if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+")))
19669231Skris		err(1, "can't open temporary file");
1971590Srgrimes	(void)unlink(tmpname);
1981590Srgrimes
1991590Srgrimes	if (!nobanner) {
20069231Skris		tty = ttyname(STDERR_FILENO);
20169231Skris		if (tty == NULL)
20266557Sn_hibma			tty = "no tty";
20366557Sn_hibma
2041590Srgrimes		if (!(whom = getlogin()))
2051590Srgrimes			whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
2061590Srgrimes		(void)gethostname(hostname, sizeof(hostname));
2071590Srgrimes		(void)time(&now);
2081590Srgrimes		lt = localtime(&now);
2091590Srgrimes
2101590Srgrimes		/*
2111590Srgrimes		 * all this stuff is to blank out a square for the message;
2121590Srgrimes		 * we wrap message lines at column 79, not 80, because some
2131590Srgrimes		 * terminals wrap after 79, some do not, and we can't tell.
2141590Srgrimes		 * Which means that we may leave a non-blank character
2151590Srgrimes		 * in column 80, but that can't be helped.
2161590Srgrimes		 */
2171590Srgrimes		(void)fprintf(fp, "\r%79s\r\n", " ");
21841717Sdillon		(void)snprintf(lbuf, sizeof(lbuf),
21941717Sdillon		    "Broadcast Message from %s@%s",
2201590Srgrimes		    whom, hostname);
2211590Srgrimes		(void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf);
22269231Skris		(void)snprintf(lbuf, sizeof(lbuf),
22366557Sn_hibma		    "        (%s) at %d:%02d %s...", tty,
22463909Sasmodai		    lt->tm_hour, lt->tm_min, lt->tm_zone);
2251590Srgrimes		(void)fprintf(fp, "%-79.79s\r\n", lbuf);
2261590Srgrimes	}
2271590Srgrimes	(void)fprintf(fp, "%79s\r\n", " ");
2281590Srgrimes
22976367Skris	if (fname) {
23076367Skris		egid = getegid();
23176367Skris		setegid(getgid());
23276367Skris	       	if (freopen(fname, "r", stdin) == NULL)
23376367Skris			err(1, "can't read %s", fname);
23476367Skris		setegid(egid);
23576367Skris	}
2361590Srgrimes	while (fgets(lbuf, sizeof(lbuf), stdin))
2371590Srgrimes		for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) {
23850776Sdbaker			if (ch == '\r') {
23950776Sdbaker				cnt = 0;
24050776Sdbaker			} else if (cnt == 79 || ch == '\n') {
2411590Srgrimes				for (; cnt < 79; ++cnt)
2421590Srgrimes					putc(' ', fp);
2431590Srgrimes				putc('\r', fp);
2441590Srgrimes				putc('\n', fp);
2451590Srgrimes				cnt = 0;
24629434Sache			} else if (((ch & 0x80) && ch < 0xA0) ||
24729434Sache				   /* disable upper controls */
24829434Sache				   (!isprint(ch) && !isspace(ch) &&
24929434Sache				    ch != '\a' && ch != '\b')
25024729Sache				  ) {
25124729Sache				if (ch & 0x80) {
25224729Sache					ch &= 0x7F;
25329434Sache					putc('M', fp);
25429434Sache					if (++cnt == 79) {
25529434Sache						putc('\r', fp);
25629434Sache						putc('\n', fp);
25729434Sache						cnt = 0;
25829434Sache					}
25929434Sache					putc('-', fp);
26029434Sache					if (++cnt == 79) {
26129434Sache						putc('\r', fp);
26229434Sache						putc('\n', fp);
26329434Sache						cnt = 0;
26429434Sache					}
26524729Sache				}
26629434Sache				if (iscntrl(ch)) {
26729434Sache					ch ^= 040;
26829434Sache					putc('^', fp);
26929434Sache					if (++cnt == 79) {
27029434Sache						putc('\r', fp);
27129434Sache						putc('\n', fp);
27229434Sache						cnt = 0;
27329434Sache					}
27429434Sache				}
27550776Sdbaker				putc(ch, fp);
27650772Sdbaker			} else {
27750772Sdbaker				putc(ch, fp);
27829434Sache			}
2791590Srgrimes		}
2801590Srgrimes	(void)fprintf(fp, "%79s\r\n", " ");
2811590Srgrimes	rewind(fp);
2821590Srgrimes
28328695Scharnier	if (fstat(fd, &sbuf))
28469231Skris		err(1, "can't stat temporary file");
2851590Srgrimes	mbufsize = sbuf.st_size;
28628695Scharnier	if (!(mbuf = malloc((u_int)mbufsize)))
28769231Skris		err(1, "out of memory");
28828695Scharnier	if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize)
28969231Skris		err(1, "can't read temporary file");
2901590Srgrimes	(void)close(fd);
2911590Srgrimes}
292