wall.c revision 242166
1219019Sgabor/*
2219019Sgabor * Copyright (c) 1988, 1990, 1993
3219019Sgabor *	The Regents of the University of California.  All rights reserved.
4219019Sgabor *
5219019Sgabor * Redistribution and use in source and binary forms, with or without
6219019Sgabor * modification, are permitted provided that the following conditions
7219019Sgabor * are met:
8219019Sgabor * 1. Redistributions of source code must retain the above copyright
9219019Sgabor *    notice, this list of conditions and the following disclaimer.
10219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright
11219019Sgabor *    notice, this list of conditions and the following disclaimer in the
12219019Sgabor *    documentation and/or other materials provided with the distribution.
13219019Sgabor * 4. Neither the name of the University nor the names of its contributors
14219019Sgabor *    may be used to endorse or promote products derived from this software
15219019Sgabor *    without specific prior written permission.
16219019Sgabor *
17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20219019Sgabor * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27219019Sgabor * SUCH DAMAGE.
28219019Sgabor */
29219019Sgabor
30219019Sgabor#include <sys/cdefs.h>
31219019Sgabor
32219019Sgabor__FBSDID("$FreeBSD: stable/9/usr.bin/wall/wall.c 242166 2012-10-27 01:20:48Z eadler $");
33219019Sgabor
34219019Sgabor#ifndef lint
35219019Sgaborstatic const char copyright[] =
36219019Sgabor"@(#) Copyright (c) 1988, 1990, 1993\n\
37219019Sgabor	The Regents of the University of California.  All rights reserved.\n";
38219019Sgabor#endif
39219019Sgabor
40219019Sgabor#ifndef lint
41219019Sgaborstatic const char sccsid[] = "@(#)wall.c	8.2 (Berkeley) 11/16/93";
42219019Sgabor#endif
43219019Sgabor
44219019Sgabor/*
45219019Sgabor * This program is not related to David Wall, whose Stanford Ph.D. thesis
46219019Sgabor * is entitled "Mechanisms for Broadcast and Selective Broadcast".
47219019Sgabor */
48219019Sgabor
49219019Sgabor#include <sys/param.h>
50219019Sgabor#include <sys/stat.h>
51219019Sgabor#include <sys/uio.h>
52219019Sgabor
53219019Sgabor#include <ctype.h>
54219019Sgabor#include <err.h>
55219019Sgabor#include <grp.h>
56219019Sgabor#include <locale.h>
57219019Sgabor#include <paths.h>
58219019Sgabor#include <pwd.h>
59219019Sgabor#include <stdio.h>
60219019Sgabor#include <stdlib.h>
61219019Sgabor#include <string.h>
62219019Sgabor#include <time.h>
63219019Sgabor#include <unistd.h>
64219019Sgabor#include <utmpx.h>
65219019Sgabor#include <wchar.h>
66219019Sgabor#include <wctype.h>
67219019Sgabor
68219019Sgabor#include "ttymsg.h"
69219019Sgabor
70219019Sgaborstatic void makemsg(char *);
71219019Sgaborstatic void usage(void);
72219019Sgabor
73219019Sgaborstruct wallgroup {
74219019Sgabor	struct wallgroup *next;
75219019Sgabor	char		*name;
76219019Sgabor	gid_t		gid;
77219019Sgabor} *grouplist;
78219019Sgaborint nobanner;
79219019Sgaborint mbufsize;
80219019Sgaborchar *mbuf;
81219019Sgabor
82219019Sgaborstatic int
83219019Sgaborttystat(char *line)
84219019Sgabor{
85219019Sgabor	struct stat sb;
86219019Sgabor	char ttybuf[MAXPATHLEN];
87219019Sgabor
88219019Sgabor	(void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line);
89219019Sgabor	if (stat(ttybuf, &sb) == 0) {
90219019Sgabor		return (0);
91219019Sgabor	} else
92219019Sgabor		return (-1);
93219019Sgabor}
94219019Sgabor
95219019Sgaborint
96219019Sgabormain(int argc, char *argv[])
97219019Sgabor{
98219019Sgabor	struct iovec iov;
99219019Sgabor	struct utmpx *utmp;
100219019Sgabor	int ch;
101219019Sgabor	int ingroup;
102219019Sgabor	struct wallgroup *g;
103219019Sgabor	struct group *grp;
104219019Sgabor	char **np;
105219019Sgabor	const char *p;
106219019Sgabor	struct passwd *pw;
107219019Sgabor
108219019Sgabor	(void)setlocale(LC_CTYPE, "");
109219019Sgabor
110219019Sgabor	while ((ch = getopt(argc, argv, "g:n")) != -1)
111219019Sgabor		switch (ch) {
112219019Sgabor		case 'n':
113219019Sgabor			/* undoc option for shutdown: suppress banner */
114219019Sgabor			if (geteuid() == 0)
115219019Sgabor				nobanner = 1;
116219019Sgabor			break;
117250984Sed		case 'g':
118219019Sgabor			g = (struct wallgroup *)malloc(sizeof *g);
119219019Sgabor			g->next = grouplist;
120219019Sgabor			g->name = optarg;
121219019Sgabor			g->gid = -1;
122219019Sgabor			grouplist = g;
123219019Sgabor			break;
124219019Sgabor		case '?':
125219019Sgabor		default:
126219019Sgabor			usage();
127219019Sgabor		}
128219019Sgabor	argc -= optind;
129219019Sgabor	argv += optind;
130219019Sgabor	if (argc > 1)
131219019Sgabor		usage();
132219019Sgabor
133219019Sgabor	for (g = grouplist; g; g = g->next) {
134219019Sgabor		grp = getgrnam(g->name);
135219019Sgabor		if (grp != NULL)
136219019Sgabor			g->gid = grp->gr_gid;
137219019Sgabor		else
138219019Sgabor			warnx("%s: no such group", g->name);
139219019Sgabor	}
140219019Sgabor
141219019Sgabor	makemsg(*argv);
142219019Sgabor
143219019Sgabor	iov.iov_base = mbuf;
144219019Sgabor	iov.iov_len = mbufsize;
145219019Sgabor	/* NOSTRICT */
146219019Sgabor	while ((utmp = getutxent()) != NULL) {
147219019Sgabor		if (utmp->ut_type != USER_PROCESS)
148219019Sgabor			continue;
149219019Sgabor		if (ttystat(utmp->ut_line) != 0)
150219019Sgabor			continue;
151219019Sgabor		if (grouplist) {
152219019Sgabor			ingroup = 0;
153219019Sgabor			pw = getpwnam(utmp->ut_user);
154219019Sgabor			if (!pw)
155219019Sgabor				continue;
156219019Sgabor			for (g = grouplist; g && ingroup == 0; g = g->next) {
157219019Sgabor				if (g->gid == (gid_t)-1)
158219019Sgabor					continue;
159219019Sgabor				if (g->gid == pw->pw_gid)
160219019Sgabor					ingroup = 1;
161219019Sgabor				else if ((grp = getgrgid(g->gid)) != NULL) {
162219019Sgabor					for (np = grp->gr_mem; *np; np++) {
163219019Sgabor						if (strcmp(*np, utmp->ut_user) == 0) {
164219019Sgabor							ingroup = 1;
165219019Sgabor							break;
166219019Sgabor						}
167219019Sgabor					}
168219019Sgabor				}
169219019Sgabor			}
170219019Sgabor			if (ingroup == 0)
171219019Sgabor				continue;
172219019Sgabor		}
173219019Sgabor		if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5)) != NULL)
174219019Sgabor			warnx("%s", p);
175219019Sgabor	}
176219019Sgabor	exit(0);
177219019Sgabor}
178219019Sgabor
179219019Sgaborstatic void
180219019Sgaborusage(void)
181219019Sgabor{
182219019Sgabor	(void)fprintf(stderr, "usage: wall [-g group] [file]\n");
183219019Sgabor	exit(1);
184219019Sgabor}
185219019Sgabor
186219019Sgaborvoid
187219019Sgabormakemsg(char *fname)
188219019Sgabor{
189219019Sgabor	int cnt;
190219019Sgabor	wchar_t ch;
191219019Sgabor	struct tm *lt;
192219019Sgabor	struct passwd *pw;
193219019Sgabor	struct stat sbuf;
194219019Sgabor	time_t now;
195219019Sgabor	FILE *fp;
196219019Sgabor	int fd;
197219019Sgabor	char hostname[MAXHOSTNAMELEN], tmpname[64];
198219019Sgabor	wchar_t *p, *tmp, lbuf[256], codebuf[13];
199219019Sgabor	const char *tty;
200219019Sgabor	const char *whom;
201219019Sgabor	gid_t egid;
202219019Sgabor
203219019Sgabor	(void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP);
204219019Sgabor	if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+")))
205219019Sgabor		err(1, "can't open temporary file");
206219019Sgabor	(void)unlink(tmpname);
207219019Sgabor
208219019Sgabor	if (!nobanner) {
209219019Sgabor		tty = ttyname(STDERR_FILENO);
210219019Sgabor		if (tty == NULL)
211219019Sgabor			tty = "no tty";
212219019Sgabor
213219019Sgabor		if (!(whom = getlogin()))
214219019Sgabor			whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???";
215219019Sgabor		(void)gethostname(hostname, sizeof(hostname));
216219019Sgabor		(void)time(&now);
217219019Sgabor		lt = localtime(&now);
218219019Sgabor
219219019Sgabor		/*
220219019Sgabor		 * all this stuff is to blank out a square for the message;
221219019Sgabor		 * we wrap message lines at column 79, not 80, because some
222219019Sgabor		 * terminals wrap after 79, some do not, and we can't tell.
223219019Sgabor		 * Which means that we may leave a non-blank character
224219019Sgabor		 * in column 80, but that can't be helped.
225219019Sgabor		 */
226219019Sgabor		(void)fwprintf(fp, L"\r%79s\r\n", " ");
227219019Sgabor		(void)swprintf(lbuf, sizeof(lbuf)/sizeof(wchar_t),
228219019Sgabor		    L"Broadcast Message from %s@%s",
229219019Sgabor		    whom, hostname);
230219019Sgabor		(void)fwprintf(fp, L"%-79.79S\007\007\r\n", lbuf);
231219019Sgabor		(void)swprintf(lbuf, sizeof(lbuf)/sizeof(wchar_t),
232219019Sgabor		    L"        (%s) at %d:%02d %s...", tty,
233219019Sgabor		    lt->tm_hour, lt->tm_min, lt->tm_zone);
234219019Sgabor		(void)fwprintf(fp, L"%-79.79S\r\n", lbuf);
235219019Sgabor	}
236219019Sgabor	(void)fwprintf(fp, L"%79s\r\n", " ");
237219019Sgabor
238219019Sgabor	if (fname) {
239219019Sgabor		egid = getegid();
240219019Sgabor		setegid(getgid());
241219019Sgabor		if (freopen(fname, "r", stdin) == NULL)
242219019Sgabor			err(1, "can't read %s", fname);
243219019Sgabor		if (setegid(egid) != 0)
244219019Sgabor			err(1, "setegid failed");
245219019Sgabor	}
246219019Sgabor	cnt = 0;
247219019Sgabor	while (fgetws(lbuf, sizeof(lbuf)/sizeof(wchar_t), stdin)) {
248219019Sgabor		for (p = lbuf; (ch = *p) != L'\0'; ++p, ++cnt) {
249219019Sgabor			if (ch == L'\r') {
250219019Sgabor				putwc(L'\r', fp);
251219019Sgabor				cnt = 0;
252219019Sgabor				continue;
253219019Sgabor			} else if (ch == L'\n') {
254219019Sgabor				for (; cnt < 79; ++cnt)
255219019Sgabor					putwc(L' ', fp);
256219019Sgabor				putwc(L'\r', fp);
257219019Sgabor				putwc(L'\n', fp);
258219019Sgabor				break;
259219019Sgabor			}
260219019Sgabor			if (cnt == 79) {
261219019Sgabor				putwc(L'\r', fp);
262219019Sgabor				putwc(L'\n', fp);
263219019Sgabor				cnt = 0;
264219019Sgabor			}
265219019Sgabor			if (iswprint(ch) || iswspace(ch) || ch == L'\a' || ch == L'\b') {
266219019Sgabor				putwc(ch, fp);
267219019Sgabor			} else {
268219019Sgabor				(void)swprintf(codebuf, sizeof(codebuf)/sizeof(wchar_t), L"<0x%X>", ch);
269219019Sgabor				for (tmp = codebuf; *tmp != L'\0'; ++tmp) {
270219019Sgabor					putwc(*tmp, fp);
271219019Sgabor					if (++cnt == 79) {
272219019Sgabor						putwc(L'\r', fp);
273219019Sgabor						putwc(L'\n', fp);
274219019Sgabor						cnt = 0;
275219019Sgabor					}
276219019Sgabor				}
277219019Sgabor				--cnt;
278219019Sgabor			}
279219019Sgabor		}
280219019Sgabor	}
281219019Sgabor	(void)fwprintf(fp, L"%79s\r\n", " ");
282219019Sgabor	rewind(fp);
283219019Sgabor
284219019Sgabor	if (fstat(fd, &sbuf))
285219019Sgabor		err(1, "can't stat temporary file");
286219019Sgabor	mbufsize = sbuf.st_size;
287219019Sgabor	if (!(mbuf = malloc((u_int)mbufsize)))
288219019Sgabor		err(1, "out of memory");
289219019Sgabor	if ((int)fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize)
290219019Sgabor		err(1, "can't read temporary file");
291219019Sgabor	(void)close(fd);
292219019Sgabor}
293219019Sgabor