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