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 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 * 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 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 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