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