wall.c revision 202200
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
3487675Smarkm#include <sys/cdefs.h>
3587675Smarkm
3687675Smarkm__FBSDID("$FreeBSD: head/usr.bin/wall/wall.c 202200 2010-01-13 18:09:54Z ed $");
3787675Smarkm
381590Srgrimes#ifndef lint
3928695Scharnierstatic const char copyright[] =
401590Srgrimes"@(#) Copyright (c) 1988, 1990, 1993\n\
411590Srgrimes	The Regents of the University of California.  All rights reserved.\n";
4287675Smarkm#endif
431590Srgrimes
441590Srgrimes#ifndef lint
4587675Smarkmstatic const char sccsid[] = "@(#)wall.c	8.2 (Berkeley) 11/16/93";
4628695Scharnier#endif
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>
68202200Sed#include <utmpx.h>
691590Srgrimes
7083242Sdd#include "ttymsg.h"
7183242Sdd
7273255Simpstatic void makemsg(char *);
7373255Simpstatic void usage(void);
741590Srgrimes
7573255Simpstruct wallgroup {
7673255Simp	struct wallgroup *next;
7773255Simp	char		*name;
7873255Simp	gid_t		gid;
7973255Simp} *grouplist;
801590Srgrimesint nobanner;
811590Srgrimesint mbufsize;
821590Srgrimeschar *mbuf;
831590Srgrimes
84155875Scognetstatic int
85200156Sedttystat(char *line)
86155875Scognet{
87155875Scognet	struct stat sb;
88155875Scognet	char ttybuf[MAXPATHLEN];
89155875Scognet
90200156Sed	(void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
91155875Scognet	if (stat(ttybuf, &sb) == 0) {
92155875Scognet		return (0);
93155875Scognet	} else
94155875Scognet		return (-1);
95155875Scognet}
96155875Scognet
971590Srgrimesint
9873255Simpmain(int argc, char *argv[])
991590Srgrimes{
1001590Srgrimes	struct iovec iov;
101200156Sed	struct utmpx *utmp;
10273255Simp	int ch;
10383082Sru	int ingroup;
10473255Simp	struct wallgroup *g;
10573255Simp	struct group *grp;
10683242Sdd	char **np;
10783242Sdd	const char *p;
10873255Simp	struct passwd *pw;
1091590Srgrimes
11025777Sache	(void)setlocale(LC_CTYPE, "");
11125777Sache
11273255Simp	while ((ch = getopt(argc, argv, "g:n")) != -1)
1131590Srgrimes		switch (ch) {
1141590Srgrimes		case 'n':
1151590Srgrimes			/* undoc option for shutdown: suppress banner */
1161590Srgrimes			if (geteuid() == 0)
1171590Srgrimes				nobanner = 1;
1181590Srgrimes			break;
11973255Simp		case 'g':
12073255Simp			g = (struct wallgroup *)malloc(sizeof *g);
12173255Simp			g->next = grouplist;
12273255Simp			g->name = optarg;
12373255Simp			g->gid = -1;
12473255Simp			grouplist = g;
12573255Simp			break;
1261590Srgrimes		case '?':
1271590Srgrimes		default:
12828695Scharnier			usage();
1291590Srgrimes		}
1301590Srgrimes	argc -= optind;
1311590Srgrimes	argv += optind;
1321590Srgrimes	if (argc > 1)
13328695Scharnier		usage();
1341590Srgrimes
13573255Simp	for (g = grouplist; g; g = g->next) {
13673255Simp		grp = getgrnam(g->name);
13773320Simp		if (grp != NULL)
13873255Simp			g->gid = grp->gr_gid;
13973320Simp		else
14073320Simp			warnx("%s: no such group", g->name);
14173255Simp	}
14273255Simp
1431590Srgrimes	makemsg(*argv);
1441590Srgrimes
1451590Srgrimes	iov.iov_base = mbuf;
1461590Srgrimes	iov.iov_len = mbufsize;
1471590Srgrimes	/* NOSTRICT */
148200156Sed	while ((utmp = getutxent()) != NULL) {
149200156Sed		if (utmp->ut_type != USER_PROCESS)
1501590Srgrimes			continue;
151200156Sed		if (ttystat(utmp->ut_line) != 0)
152155875Scognet			continue;
15373255Simp		if (grouplist) {
15483082Sru			ingroup = 0;
155200156Sed			pw = getpwnam(utmp->ut_user);
15673255Simp			if (!pw)
15773255Simp				continue;
15873255Simp			for (g = grouplist; g && ingroup == 0; g = g->next) {
15987675Smarkm				if (g->gid == (gid_t)-1)
16073255Simp					continue;
16173255Simp				if (g->gid == pw->pw_gid)
16273255Simp					ingroup = 1;
16383082Sru				else if ((grp = getgrgid(g->gid)) != NULL) {
16483082Sru					for (np = grp->gr_mem; *np; np++) {
165200156Sed						if (strcmp(*np, utmp->ut_user) == 0) {
16683082Sru							ingroup = 1;
16783082Sru							break;
16883082Sru						}
16983082Sru					}
17083082Sru				}
17173255Simp			}
17273255Simp			if (ingroup == 0)
17373255Simp				continue;
17473255Simp		}
175200156Sed		if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5)) != NULL)
17628695Scharnier			warnx("%s", p);
1771590Srgrimes	}
1781590Srgrimes	exit(0);
1791590Srgrimes}
1801590Srgrimes
18128695Scharnierstatic void
182201224Sedusage(void)
18328695Scharnier{
18473320Simp	(void)fprintf(stderr, "usage: wall [-g group] [file]\n");
18528695Scharnier	exit(1);
18628695Scharnier}
18728695Scharnier
1881590Srgrimesvoid
18973255Simpmakemsg(char *fname)
1901590Srgrimes{
19173255Simp	int cnt;
19273255Simp	unsigned char ch;
1931590Srgrimes	struct tm *lt;
1941590Srgrimes	struct passwd *pw;
1951590Srgrimes	struct stat sbuf;
19629434Sache	time_t now;
1971590Srgrimes	FILE *fp;
1981590Srgrimes	int fd;
19987675Smarkm	char *p, hostname[MAXHOSTNAMELEN], lbuf[256], tmpname[64];
20087675Smarkm	const char *tty;
20169231Skris	const char *whom;
20276367Skris	gid_t egid;
2031590Srgrimes
20469231Skris	(void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP);
20569231Skris	if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+")))
20669231Skris		err(1, "can't open temporary file");
2071590Srgrimes	(void)unlink(tmpname);
2081590Srgrimes
2091590Srgrimes	if (!nobanner) {
21069231Skris		tty = ttyname(STDERR_FILENO);
21169231Skris		if (tty == NULL)
21266557Sn_hibma			tty = "no tty";
21366557Sn_hibma
2141590Srgrimes		if (!(whom = getlogin()))
2151590Srgrimes			whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
2161590Srgrimes		(void)gethostname(hostname, sizeof(hostname));
2171590Srgrimes		(void)time(&now);
2181590Srgrimes		lt = localtime(&now);
2191590Srgrimes
2201590Srgrimes		/*
2211590Srgrimes		 * all this stuff is to blank out a square for the message;
2221590Srgrimes		 * we wrap message lines at column 79, not 80, because some
2231590Srgrimes		 * terminals wrap after 79, some do not, and we can't tell.
2241590Srgrimes		 * Which means that we may leave a non-blank character
2251590Srgrimes		 * in column 80, but that can't be helped.
2261590Srgrimes		 */
2271590Srgrimes		(void)fprintf(fp, "\r%79s\r\n", " ");
22841717Sdillon		(void)snprintf(lbuf, sizeof(lbuf),
22941717Sdillon		    "Broadcast Message from %s@%s",
2301590Srgrimes		    whom, hostname);
2311590Srgrimes		(void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf);
23269231Skris		(void)snprintf(lbuf, sizeof(lbuf),
23366557Sn_hibma		    "        (%s) at %d:%02d %s...", tty,
23463909Sasmodai		    lt->tm_hour, lt->tm_min, lt->tm_zone);
2351590Srgrimes		(void)fprintf(fp, "%-79.79s\r\n", lbuf);
2361590Srgrimes	}
2371590Srgrimes	(void)fprintf(fp, "%79s\r\n", " ");
2381590Srgrimes
23976367Skris	if (fname) {
24076367Skris		egid = getegid();
24176367Skris		setegid(getgid());
24276367Skris	       	if (freopen(fname, "r", stdin) == NULL)
24376367Skris			err(1, "can't read %s", fname);
24476367Skris		setegid(egid);
24576367Skris	}
246175346Sdas	while (fgets(lbuf, sizeof(lbuf), stdin)) {
2471590Srgrimes		for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) {
24850776Sdbaker			if (ch == '\r') {
249175346Sdas				putc('\r', fp);
25050776Sdbaker				cnt = 0;
251175346Sdas				continue;
252175346Sdas			} else if (ch == '\n') {
2531590Srgrimes				for (; cnt < 79; ++cnt)
2541590Srgrimes					putc(' ', fp);
2551590Srgrimes				putc('\r', fp);
2561590Srgrimes				putc('\n', fp);
257175346Sdas				break;
258175346Sdas			}
259175346Sdas			if (cnt == 79) {
260175346Sdas				putc('\r', fp);
261175346Sdas				putc('\n', fp);
2621590Srgrimes				cnt = 0;
263175346Sdas			}
264175346Sdas			if (((ch & 0x80) && ch < 0xA0) ||
26529434Sache				   /* disable upper controls */
26629434Sache				   (!isprint(ch) && !isspace(ch) &&
26729434Sache				    ch != '\a' && ch != '\b')
26824729Sache				  ) {
26924729Sache				if (ch & 0x80) {
27024729Sache					ch &= 0x7F;
27129434Sache					putc('M', fp);
27229434Sache					if (++cnt == 79) {
27329434Sache						putc('\r', fp);
27429434Sache						putc('\n', fp);
27529434Sache						cnt = 0;
27629434Sache					}
27729434Sache					putc('-', fp);
27829434Sache					if (++cnt == 79) {
27929434Sache						putc('\r', fp);
28029434Sache						putc('\n', fp);
28129434Sache						cnt = 0;
28229434Sache					}
28324729Sache				}
28429434Sache				if (iscntrl(ch)) {
28529434Sache					ch ^= 040;
28629434Sache					putc('^', fp);
28729434Sache					if (++cnt == 79) {
28829434Sache						putc('\r', fp);
28929434Sache						putc('\n', fp);
29029434Sache						cnt = 0;
29129434Sache					}
29229434Sache				}
29329434Sache			}
294175346Sdas			putc(ch, fp);
2951590Srgrimes		}
296175346Sdas	}
2971590Srgrimes	(void)fprintf(fp, "%79s\r\n", " ");
2981590Srgrimes	rewind(fp);
2991590Srgrimes
30028695Scharnier	if (fstat(fd, &sbuf))
30169231Skris		err(1, "can't stat temporary file");
3021590Srgrimes	mbufsize = sbuf.st_size;
30328695Scharnier	if (!(mbuf = malloc((u_int)mbufsize)))
30469231Skris		err(1, "out of memory");
30587675Smarkm	if ((int)fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize)
30669231Skris		err(1, "can't read temporary file");
3071590Srgrimes	(void)close(fd);
3081590Srgrimes}
309