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 || !nonuser(*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) strncpy(user, name, NMAX);
 471         user[NMAX] = '\0';
 472         for (i = 0; pgrp->gr_mem[i] && pgrp->gr_mem[i][0]; i++) {
 473                 if (strcmp(user, pgrp->gr_mem[i]) == 0)
 474                         return (1);
 475         }
 476 
 477         return (0);
 478 }
 479 
 480 static int
 481 init_template(void) {
 482         int fd = 0;
 483         int err = 0;
 484 
 485         fd = open64(CTFS_ROOT "/process/template", O_RDWR);
 486         if (fd == -1)
 487                 return (-1);
 488 
 489         err |= ct_tmpl_set_critical(fd, 0);
 490         err |= ct_tmpl_set_informative(fd, 0);
 491         err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
 492         err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
 493         if (err || ct_tmpl_activate(fd)) {
 494                 (void) close(fd);
 495                 return (-1);
 496         }
 497 
 498         return (fd);
 499 }