1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
23/*	  All Rights Reserved  	*/
24
25
26
27/*
28 * Copyright 1988-2003 Sun Microsystems, Inc.  All rights reserved.
29 * Use is subject to license terms.
30 */
31
32#pragma ident	"%Z%%M%	%I%	%E% SMI"
33
34#include <signal.h>
35#include <stdio.h>
36#include <grp.h>
37#include <sys/types.h>
38#include <unistd.h>
39#include <stdlib.h>
40#include <string.h>
41#include <ctype.h>
42#include <sys/stat.h>
43#include <utmpx.h>
44#include <sys/utsname.h>
45#include <dirent.h>
46#include <pwd.h>
47#include <fcntl.h>
48#include <time.h>
49#include <errno.h>
50#include <locale.h>
51#include <syslog.h>
52#include <sys/wait.h>
53#include <limits.h>
54
55/*
56 * utmpx defines wider fields for user and line.  For compatibility of output,
57 * we are limiting these to the old maximums in utmp. Define UTMPX_NAMELEN
58 * to use the full lengths.
59 */
60#ifndef UTMPX_NAMELEN
61/* XXX - utmp -fix name length */
62#define	NMAX	(_POSIX_LOGIN_NAME_MAX - 1)
63#define	LMAX	12
64#else /* UTMPX_NAMELEN */
65#define	NMAX	(sizeof (((struct utmpx *)0)->ut_user)
66#define	LMAX	(sizeof (((struct utmpx *)0)->ut_line)
67#endif /* UTMPX_NAMELEN */
68
69static char	mesg[3000];
70static char	*infile;
71static int	gflag;
72static struct	group *pgrp;
73static char	*grpname;
74static char	line[MAXNAMLEN+1] = "???";
75static char	systm[MAXNAMLEN+1];
76static time_t	tloc;
77static struct	utsname utsn;
78static char	who[9]	= "???";
79static char	time_buf[50];
80#define	DATE_FMT	"%a %b %e %H:%M:%S"
81
82static void sendmes(struct utmpx *);
83static int chkgrp(char *);
84static char *copy_str_till(char *, char *, char, int);
85
86int
87main(int argc, char *argv[])
88{
89	int	i = 0;
90	struct utmpx *p;
91	FILE	*f;
92	char	*ptr, *start;
93	struct	passwd *pwd;
94	char	*term_name;
95	int	c;
96	int	aflag = 0;
97	int	errflg = 0;
98
99	(void) setlocale(LC_ALL, "");
100
101	while ((c = getopt(argc, argv, "g:a")) != EOF)
102		switch (c) {
103		case 'a':
104			aflag++;
105			break;
106		case 'g':
107			if (gflag) {
108				(void) fprintf(stderr,
109				    "Only one group allowed\n");
110				exit(1);
111			}
112			if ((pgrp = getgrnam(grpname = optarg)) == NULL) {
113				(void) fprintf(stderr, "Unknown group %s\n",
114				    grpname);
115				exit(1);
116			}
117			gflag++;
118			break;
119		case '?':
120			errflg++;
121			break;
122		}
123
124	if (errflg) {
125		(void) fprintf(stderr,
126		    "Usage: wall [-a] [-g group] [files...]\n");
127		return (1);
128	}
129
130	if (optind < argc)
131		infile = argv[optind];
132
133	if (uname(&utsn) == -1) {
134		(void) fprintf(stderr, "wall: uname() failed, %s\n",
135		    strerror(errno));
136		exit(2);
137	}
138	(void) strcpy(systm, utsn.nodename);
139
140	/*
141	 * Get the name of the terminal wall is running from.
142	 */
143
144	if ((term_name = ttyname(fileno(stderr))) != NULL) {
145		/*
146		 * skip the leading "/dev/" in term_name
147		 */
148		(void) strncpy(line, &term_name[5], sizeof (line) - 1);
149	}
150
151	if (who[0] == '?') {
152		if (pwd = getpwuid(getuid()))
153			(void) strncpy(&who[0], pwd->pw_name, sizeof (who));
154	}
155
156	f = stdin;
157	if (infile) {
158		f = fopen(infile, "r");
159		if (f == NULL) {
160			(void) fprintf(stderr, "Cannot open %s\n", infile);
161			exit(1);
162		}
163	}
164
165	start = &mesg[0];
166	ptr = start;
167	while ((ptr - start) < 3000) {
168		size_t n;
169
170		if (fgets(ptr, &mesg[sizeof (mesg)] - ptr, f) == NULL)
171			break;
172		if ((n = strlen(ptr)) == 0)
173			break;
174		ptr += n;
175	}
176	(void) fclose(f);
177
178	/*
179	 * If the request is from the rwall daemon then use the caller's
180	 * name and host.  We determine this if all of the following is true:
181	 *	1) First 5 characters are "From "
182	 *	2) Next non-white characters are of the form "name@host:"
183	 */
184	if (strcmp(line, "???") == 0) {
185		char rwho[MAXNAMLEN+1];
186		char rsystm[MAXNAMLEN+1];
187		char *cp;
188
189		if (strncmp(mesg, "From ", 5) == 0) {
190			cp = &mesg[5];
191			cp = copy_str_till(rwho, cp, '@', MAXNAMLEN + 1);
192			if (rwho[0] != '\0') {
193				cp = copy_str_till(rsystm, ++cp, ':',
194				    MAXNAMLEN + 1);
195				if (rsystm[0] != '\0') {
196					(void) strcpy(systm, rsystm);
197					(void) strncpy(rwho, who, 9);
198					(void) strcpy(line, "rpc.rwalld");
199				}
200			}
201		}
202	}
203	(void) time(&tloc);
204	(void) strftime(time_buf, sizeof (time_buf),
205			    DATE_FMT, localtime(&tloc));
206
207	setutxent();
208	while ((p = getutxent()) != NULL) {
209		if (p->ut_type != USER_PROCESS)
210			continue;
211		/*
212		 * if (-a option OR NOT pty window login), send the message
213		 */
214		if (aflag || !nonuser(*p))
215			sendmes(p);
216	}
217	endutxent();
218
219	(void) alarm(60);
220	do {
221		i = (int)wait((int *)0);
222	} while (i != -1 || errno != ECHILD);
223
224	return (0);
225}
226
227/*
228 * Copy src to destination upto but not including the delim.
229 * Leave dst empty if delim not found or whitespace encountered.
230 * Return pointer to next character (delim, whitespace, or '\0')
231 */
232static char *
233copy_str_till(char *dst, char *src, char delim, int len)
234{
235	int i = 0;
236
237	while (*src != '\0' && i < len) {
238		if (isspace(*src)) {
239			dst[0] = '\0';
240			return (src);
241		}
242		if (*src == delim) {
243			dst[i] = '\0';
244			return (src);
245		}
246		dst[i++] = *src++;
247	}
248	dst[0] = '\0';
249	return (src);
250}
251
252/*
253 * Note to future maintainers: with the change of wall to use the
254 * getutxent() API, the forked children (created by this function)
255 * must call _exit as opposed to exit. This is necessary to avoid
256 * unwanted fflushing of getutxent's stdio stream (caused by atexit
257 * processing).
258 */
259static void
260sendmes(struct utmpx *p)
261{
262	int i;
263	char *s;
264	static char device[LMAX + 6];
265	char *bp;
266	int ibp;
267	FILE *f;
268	int fd;
269
270	if (gflag)
271		if (!chkgrp(p->ut_user))
272			return;
273	while ((i = (int)fork()) == -1) {
274		(void) alarm(60);
275		(void) wait((int *)0);
276		(void) alarm(0);
277	}
278
279	if (i)
280		return;
281
282	(void) signal(SIGHUP, SIG_IGN);
283	(void) alarm(60);
284	s = &device[0];
285	(void) snprintf(s, sizeof (device), "/dev/%.*s", LMAX, p->ut_line);
286
287	/* check if the device is really a tty */
288	if ((fd = open(s, O_WRONLY|O_NOCTTY|O_NONBLOCK)) == -1) {
289		(void) fprintf(stderr, "Cannot send to %.*s on %s\n",
290		    NMAX, p->ut_user, s);
291		perror("open");
292		(void) fflush(stderr);
293		_exit(1);
294	} else {
295		if (!isatty(fd)) {
296			(void) fprintf(stderr,
297			    "Cannot send to device %.*s %s\n",
298			    LMAX, p->ut_line,
299			    "because it's not a tty");
300			openlog("wall", 0, LOG_AUTH);
301			syslog(LOG_CRIT, "%.*s in utmpx is not a tty\n",
302			    LMAX, p->ut_line);
303			closelog();
304			(void) fflush(stderr);
305			_exit(1);
306		}
307	}
308#ifdef DEBUG
309	(void) close(fd);
310	f = fopen("wall.debug", "a");
311#else
312	f = fdopen(fd, "w");
313#endif
314	if (f == NULL) {
315		(void) fprintf(stderr, "Cannot send to %-.*s on %s\n",
316		    NMAX, &p->ut_user[0], s);
317		perror("open");
318		(void) fflush(stderr);
319		_exit(1);
320	}
321	(void) fprintf(f,
322	    "\07\07\07Broadcast Message from %s (%s) on %s %19.19s",
323	    who, line, systm, time_buf);
324	if (gflag)
325		(void) fprintf(f, " to group %s", grpname);
326	(void) fprintf(f, "...\n");
327#ifdef DEBUG
328	(void) fprintf(f, "DEBUG: To %.8s on %s\n", p->ut_user, s);
329#endif
330	i = strlen(mesg);
331	for (bp = mesg; --i >= 0; bp++) {
332		ibp = (unsigned int)((unsigned char) *bp);
333		if (*bp == '\n')
334			(void) putc('\r', f);
335		if (isprint(ibp) || *bp == '\r' || *bp == '\013' ||
336		    *bp == ' ' || *bp == '\t' || *bp == '\n' || *bp == '\007') {
337			(void) putc(*bp, f);
338		} else {
339			if (!isascii(*bp)) {
340				(void) fputs("M-", f);
341				*bp = toascii(*bp);
342			}
343			if (iscntrl(*bp)) {
344				(void) putc('^', f);
345				(void) putc(*bp + 0100, f);
346			}
347			else
348				(void) putc(*bp, f);
349		}
350
351		if (*bp == '\n')
352			(void) fflush(f);
353
354		if (ferror(f) || feof(f)) {
355			(void) printf("\n\007Write failed\n");
356			(void) fflush(stdout);
357			_exit(1);
358		}
359	}
360	(void) fclose(f);
361	(void) close(fd);
362	_exit(0);
363}
364
365
366static int
367chkgrp(char *name)
368{
369	int i;
370	char *p;
371
372	for (i = 0; pgrp->gr_mem[i] && pgrp->gr_mem[i][0]; i++) {
373		for (p = name; *p && *p != ' '; p++);
374		*p = 0;
375		if (strncmp(name, pgrp->gr_mem[i], 8) == 0)
376			return (1);
377	}
378
379	return (0);
380}
381