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
69 static char mesg[3000];
70 static char *infile;
71 static int gflag;
72 static struct group *pgrp;
73 static char *grpname;
74 static char line[MAXNAMLEN+1] = "???";
75 static char systm[MAXNAMLEN+1];
76 static time_t tloc;
77 static struct utsname utsn;
78 static char who[9] = "???";
79 static char time_buf[50];
80 #define DATE_FMT "%a %b %e %H:%M:%S"
81
82 static void sendmes(struct utmpx *);
83 static int chkgrp(char *);
84 static char *copy_str_till(char *, char *, char, int);
85
86 int
main(int argc,char * argv[])87 main(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 */
232 static char *
copy_str_till(char * dst,char * src,char delim,int len)233 copy_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 */
259 static void
sendmes(struct utmpx * p)260 sendmes(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
366 static int
chkgrp(char * name)367 chkgrp(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