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 /* 33 * Copyright 2012 Joyent, Inc. All rights reserved. 34 * 35 * Copyright (c) 2013 Gary Mills 36 */ 37 38 #include <signal.h> 39 #include <stdio.h> 40 #include <stdlib.h> 41 #include <grp.h> 42 #include <sys/types.h> 43 #include <unistd.h> 44 #include <string.h> 45 #include <ctype.h> 46 #include <sys/stat.h> 47 #include <utmpx.h> 48 #include <sys/utsname.h> 49 #include <dirent.h> 50 #include <pwd.h> 51 #include <fcntl.h> 52 #include <time.h> 53 #include <errno.h> 54 #include <locale.h> 55 #include <syslog.h> 56 #include <sys/wait.h> 57 #include <limits.h> 58 #include <libzonecfg.h> 59 #include <zone.h> 60 #include <sys/contract/process.h> 61 #include <libcontract.h> 62 #include <sys/ctfs.h> 63 64 /* 65 * Use the full lengths from utmpx for user and line. 66 */ 67 #define NMAX (sizeof (((struct utmpx *)0)->ut_user)) 68 #define LMAX (sizeof (((struct utmpx *)0)->ut_line)) 69 70 static char mesg[3000]; 71 static char *infile; 72 static int gflag; 73 static struct group *pgrp; 74 static char *grpname; 75 static char line[MAXNAMLEN+1] = "???"; 76 static char systm[MAXNAMLEN+1]; 77 static time_t tloc; 78 static struct utsname utsn; 79 static char who[NMAX+1] = "???"; 80 static char time_buf[50]; 81 #define DATE_FMT "%a %b %e %H:%M:%S" 82 83 static void sendmes(struct utmpx *, zoneid_t); 84 static void sendmes_tozone(zoneid_t, int); 85 static int chkgrp(char *); 86 static char *copy_str_till(char *, char *, char, int); 87 88 static int init_template(void); 89 int contract_abandon_id(ctid_t); 90 91 int 92 main(int argc, char *argv[]) 93 { 94 FILE *f; 95 char *ptr, *start; 96 struct passwd *pwd; 97 char *term_name; 98 int c; 99 int aflag = 0; 100 int errflg = 0; 101 int zflg = 0; 102 int Zflg = 0; 103 104 char *zonename = NULL; 105 zoneid_t *zoneidlist = NULL; 106 uint_t nzids_saved, nzids = 0; 107 108 (void) setlocale(LC_ALL, ""); 109 110 while ((c = getopt(argc, argv, "g:az:Z")) != EOF) 111 switch (c) { 112 case 'a': 113 aflag++; 114 break; 115 case 'g': 116 if (gflag) { 117 (void) fprintf(stderr, 118 "Only one group allowed\n"); 119 return (1); 120 } 121 if ((pgrp = getgrnam(grpname = optarg)) == NULL) { 122 (void) fprintf(stderr, "Unknown group %s\n", 123 grpname); 124 return (1); 125 } 126 gflag++; 127 break; 128 case 'z': 129 zflg++; 130 zonename = optarg; 131 if (getzoneidbyname(zonename) == -1) { 132 (void) fprintf(stderr, "Specified zone %s " 133 "is invalid", zonename); 134 return (1); 135 } 136 break; 137 case 'Z': 138 Zflg++; 139 break; 140 case '?': 141 errflg++; 142 break; 143 } 144 145 if (errflg) { 146 (void) fprintf(stderr, 147 "Usage: wall [-a] [-g group] [-z zone] [-Z] [files...]\n"); 148 return (1); 149 } 150 151 if (zflg && Zflg) { 152 (void) fprintf(stderr, "Cannot use -z with -Z\n"); 153 return (1); 154 } 155 156 if (optind < argc) 157 infile = argv[optind]; 158 159 if (uname(&utsn) == -1) { 160 (void) fprintf(stderr, "wall: uname() failed, %s\n", 161 strerror(errno)); 162 return (2); 163 } 164 (void) strcpy(systm, utsn.nodename); 165 166 /* 167 * Get the name of the terminal wall is running from. 168 */ 169 170 if ((term_name = ttyname(fileno(stderr))) != NULL) { 171 /* 172 * skip the leading "/dev/" in term_name 173 */ 174 (void) strncpy(line, &term_name[5], sizeof (line) - 1); 175 } 176 177 if (who[0] == '?') { 178 if (pwd = getpwuid(getuid())) 179 (void) strncpy(&who[0], pwd->pw_name, sizeof (who)); 180 } 181 182 f = stdin; 183 if (infile) { 184 f = fopen(infile, "r"); 185 if (f == NULL) { 186 (void) fprintf(stderr, "Cannot open %s\n", infile); 187 return (1); 188 } 189 } 190 191 start = &mesg[0]; 192 ptr = start; 193 while ((ptr - start) < 3000) { 194 size_t n; 195 196 if (fgets(ptr, &mesg[sizeof (mesg)] - ptr, f) == NULL) 197 break; 198 if ((n = strlen(ptr)) == 0) 199 break; 200 ptr += n; 201 } 202 (void) fclose(f); 203 204 /* 205 * If the request is from the rwall daemon then use the caller's 206 * name and host. We determine this if all of the following is true: 207 * 1) First 5 characters are "From " 208 * 2) Next non-white characters are of the form "name@host:" 209 */ 210 if (strcmp(line, "???") == 0) { 211 char rwho[MAXNAMLEN+1]; 212 char rsystm[MAXNAMLEN+1]; 213 char *cp; 214 215 if (strncmp(mesg, "From ", 5) == 0) { 216 cp = &mesg[5]; 217 cp = copy_str_till(rwho, cp, '@', MAXNAMLEN + 1); 218 if (rwho[0] != '\0') { 219 cp = copy_str_till(rsystm, ++cp, ':', 220 MAXNAMLEN + 1); 221 if (rsystm[0] != '\0') { 222 (void) strcpy(systm, rsystm); 223 (void) strncpy(rwho, who, 224 sizeof (who)); 225 (void) strcpy(line, "rpc.rwalld"); 226 } 227 } 228 } 229 } 230 (void) time(&tloc); 231 (void) strftime(time_buf, sizeof (time_buf), 232 DATE_FMT, localtime(&tloc)); 233 234 if (zflg != 0) { 235 if ((zoneidlist = 236 malloc(sizeof (zoneid_t))) == NULL || 237 (*zoneidlist = getzoneidbyname(zonename)) == -1) 238 return (errno); 239 nzids = 1; 240 } else if (Zflg != 0) { 241 if (zone_list(NULL, &nzids) != 0) 242 return (errno); 243 again: 244 nzids *= 2; 245 if ((zoneidlist = malloc(nzids * sizeof (zoneid_t))) == NULL) 246 exit(errno); 247 nzids_saved = nzids; 248 if (zone_list(zoneidlist, &nzids) != 0) { 249 (void) free(zoneidlist); 250 return (errno); 251 } 252 if (nzids > nzids_saved) { 253 free(zoneidlist); 254 goto again; 255 } 256 } 257 if (zflg || Zflg) { 258 for (; nzids > 0; --nzids) 259 sendmes_tozone(zoneidlist[nzids-1], aflag); 260 free(zoneidlist); 261 } else 262 sendmes_tozone(getzoneid(), aflag); 263 264 return (0); 265 } 266 267 /* 268 * Copy src to destination upto but not including the delim. 269 * Leave dst empty if delim not found or whitespace encountered. 270 * Return pointer to next character (delim, whitespace, or '\0') 271 */ 272 static char * 273 copy_str_till(char *dst, char *src, char delim, int len) 274 { 275 int i = 0; 276 277 while (*src != '\0' && i < len) { 278 if (isspace(*src)) { 279 dst[0] = '\0'; 280 return (src); 281 } 282 if (*src == delim) { 283 dst[i] = '\0'; 284 return (src); 285 } 286 dst[i++] = *src++; 287 } 288 dst[0] = '\0'; 289 return (src); 290 } 291 292 static void 293 sendmes_tozone(zoneid_t zid, int aflag) { 294 int i = 0; 295 char zonename[ZONENAME_MAX], root[MAXPATHLEN]; 296 struct utmpx *p; 297 298 if (zid != getzoneid()) { 299 root[0] = '\0'; 300 (void) getzonenamebyid(zid, zonename, ZONENAME_MAX); 301 (void) zone_get_rootpath(zonename, root, sizeof (root)); 302 (void) strlcat(root, UTMPX_FILE, sizeof (root)); 303 if (!utmpxname(root)) { 304 (void) fprintf(stderr, "Cannot open %s\n", root); 305 return; 306 } 307 } else { 308 (void) utmpxname(UTMPX_FILE); 309 } 310 setutxent(); 311 while ((p = getutxent()) != NULL) { 312 if (p->ut_type != USER_PROCESS) 313 continue; 314 /* 315 * if (-a option OR NOT pty window login), send the message 316 */ 317 if (aflag || !nonuserx(*p)) 318 sendmes(p, zid); 319 } 320 endutxent(); 321 322 (void) alarm(60); 323 do { 324 i = (int)wait((int *)0); 325 } while (i != -1 || errno != ECHILD); 326 327 } 328 329 /* 330 * Note to future maintainers: with the change of wall to use the 331 * getutxent() API, the forked children (created by this function) 332 * must call _exit as opposed to exit. This is necessary to avoid 333 * unwanted fflushing of getutxent's stdio stream (caused by atexit 334 * processing). 335 */ 336 static void 337 sendmes(struct utmpx *p, zoneid_t zid) 338 { 339 int i; 340 char *s; 341 static char device[LMAX + 6]; 342 char *bp; 343 int ibp; 344 FILE *f; 345 int fd, tmpl_fd; 346 boolean_t zoneenter = B_FALSE; 347 348 if (zid != getzoneid()) { 349 zoneenter = B_TRUE; 350 tmpl_fd = init_template(); 351 if (tmpl_fd == -1) { 352 (void) fprintf(stderr, "Could not initialize " 353 "process contract"); 354 return; 355 } 356 } 357 358 while ((i = (int)fork()) == -1) { 359 (void) alarm(60); 360 (void) wait((int *)0); 361 (void) alarm(0); 362 } 363 364 if (i) 365 return; 366 367 if (zoneenter && zone_enter(zid) == -1) { 368 char zonename[ZONENAME_MAX]; 369 (void) getzonenamebyid(zid, zonename, ZONENAME_MAX); 370 (void) fprintf(stderr, "Could not enter zone " 371 "%s\n", zonename); 372 } 373 if (zoneenter) 374 (void) ct_tmpl_clear(tmpl_fd); 375 376 if (gflag) 377 if (!chkgrp(p->ut_user)) 378 _exit(0); 379 380 (void) signal(SIGHUP, SIG_IGN); 381 (void) alarm(60); 382 s = &device[0]; 383 (void) snprintf(s, sizeof (device), "/dev/%.*s", LMAX, p->ut_line); 384 385 /* check if the device is really a tty */ 386 if ((fd = open(s, O_WRONLY|O_NOCTTY|O_NONBLOCK)) == -1) { 387 (void) fprintf(stderr, "Cannot send to %.*s on %s\n", 388 NMAX, p->ut_user, s); 389 perror("open"); 390 (void) fflush(stderr); 391 _exit(1); 392 } else { 393 if (!isatty(fd)) { 394 (void) fprintf(stderr, 395 "Cannot send to device %.*s %s\n", 396 LMAX, p->ut_line, 397 "because it's not a tty"); 398 openlog("wall", 0, LOG_AUTH); 399 syslog(LOG_CRIT, "%.*s in utmpx is not a tty\n", 400 LMAX, p->ut_line); 401 closelog(); 402 (void) fflush(stderr); 403 _exit(1); 404 } 405 } 406 #ifdef DEBUG 407 (void) close(fd); 408 f = fopen("wall.debug", "a"); 409 #else 410 f = fdopen(fd, "w"); 411 #endif 412 if (f == NULL) { 413 (void) fprintf(stderr, "Cannot send to %-.*s on %s\n", 414 NMAX, &p->ut_user[0], s); 415 perror("open"); 416 (void) fflush(stderr); 417 _exit(1); 418 } 419 (void) fprintf(f, 420 "\07\07\07Broadcast Message from %s (%s) on %s %19.19s", 421 who, line, systm, time_buf); 422 if (gflag) 423 (void) fprintf(f, " to group %s", grpname); 424 (void) fprintf(f, "...\n"); 425 #ifdef DEBUG 426 (void) fprintf(f, "DEBUG: To %.*s on %s\n", NMAX, p->ut_user, s); 427 #endif 428 i = strlen(mesg); 429 for (bp = mesg; --i >= 0; bp++) { 430 ibp = (unsigned int)((unsigned char) *bp); 431 if (*bp == '\n') 432 (void) putc('\r', f); 433 if (isprint(ibp) || *bp == '\r' || *bp == '\013' || 434 *bp == ' ' || *bp == '\t' || *bp == '\n' || *bp == '\007') { 435 (void) putc(*bp, f); 436 } else { 437 if (!isascii(*bp)) { 438 (void) fputs("M-", f); 439 *bp = toascii(*bp); 440 } 441 if (iscntrl(*bp)) { 442 (void) putc('^', f); 443 (void) putc(*bp + 0100, f); 444 } 445 else 446 (void) putc(*bp, f); 447 } 448 449 if (*bp == '\n') 450 (void) fflush(f); 451 452 if (ferror(f) || feof(f)) { 453 (void) printf("\n\007Write failed\n"); 454 (void) fflush(stdout); 455 _exit(1); 456 } 457 } 458 (void) fclose(f); 459 (void) close(fd); 460 _exit(0); 461 } 462 463 464 static int 465 chkgrp(char *name) 466 { 467 int i; 468 char user[NMAX + 1]; 469 470 (void) strlcpy(user, name, sizeof (user)); 471 for (i = 0; pgrp->gr_mem[i] && pgrp->gr_mem[i][0]; i++) { 472 if (strcmp(user, pgrp->gr_mem[i]) == 0) 473 return (1); 474 } 475 476 return (0); 477 } 478 479 static int 480 init_template(void) { 481 int fd = 0; 482 int err = 0; 483 484 fd = open64(CTFS_ROOT "/process/template", O_RDWR); 485 if (fd == -1) 486 return (-1); 487 488 err |= ct_tmpl_set_critical(fd, 0); 489 err |= ct_tmpl_set_informative(fd, 0); 490 err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR); 491 err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT); 492 if (err || ct_tmpl_activate(fd)) { 493 (void) close(fd); 494 return (-1); 495 } 496 497 return (fd); 498 }