1 /* $OpenBSD: wall.c,v 1.35 2021/07/12 15:09:20 beck Exp $ */ 2 /* $NetBSD: wall.c,v 1.6 1994/11/17 07:17:58 jtc Exp $ */ 3 4 /* 5 * Copyright (c) 1988, 1990, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 /* 34 * This program is not related to David Wall, whose Stanford Ph.D. thesis 35 * is entitled "Mechanisms for Broadcast and Selective Broadcast". 36 */ 37 38 #include <sys/queue.h> 39 #include <sys/stat.h> 40 #include <sys/time.h> 41 #include <sys/uio.h> 42 43 #include <ctype.h> 44 #include <err.h> 45 #include <grp.h> 46 #include <limits.h> 47 #include <paths.h> 48 #include <pwd.h> 49 #include <stdio.h> 50 #include <stdlib.h> 51 #include <string.h> 52 #include <unistd.h> 53 #include <utmp.h> 54 55 struct wallgroup { 56 gid_t gid; 57 char *name; 58 char **mem; 59 struct wallgroup *next; 60 } *grouplist; 61 62 struct utmptty { 63 char *tty; 64 SLIST_ENTRY(utmptty) next; 65 }; 66 67 void makemsg(char *); 68 void addgroup(struct group *, char *); 69 char *ttymsg(struct iovec *, int, char *, int); 70 __dead void usage(void); 71 static int isu8cont(unsigned char); 72 73 int nobanner; 74 int mbufsize; 75 char *mbuf; 76 77 /* ARGSUSED */ 78 int 79 main(int argc, char **argv) 80 { 81 FILE *fp; 82 int ch, ingroup; 83 struct iovec iov; 84 struct utmp utmp; 85 char *p, **mem; 86 char line[sizeof(utmp.ut_line) + 1]; 87 char username[sizeof(utmp.ut_name) + 1]; 88 struct passwd *pw; 89 struct group *grp; 90 struct wallgroup *g; 91 struct utmptty *un; 92 SLIST_HEAD(,utmptty) utmphead; 93 SLIST_INIT(&utmphead); 94 95 while ((ch = getopt(argc, argv, "ng:")) != -1) 96 switch (ch) { 97 case 'n': 98 /* undoc option for shutdown: suppress banner */ 99 pw = getpwnam("nobody"); 100 if (geteuid() == 0 || (pw && getuid() == pw->pw_uid)) 101 nobanner = 1; 102 break; 103 case 'g': 104 if ((grp = getgrnam(optarg)) == NULL) 105 errx(1, "unknown group `%s'", optarg); 106 addgroup(grp, optarg); 107 break; 108 default: 109 usage(); 110 } 111 argc -= optind; 112 argv += optind; 113 if (argc > 1) 114 usage(); 115 116 makemsg(*argv); 117 118 if (unveil(_PATH_UTMP, "r") == -1) 119 err(1, "unveil %s", _PATH_UTMP); 120 if (unveil(_PATH_DEV, "w") == -1) 121 err(1, "unveil %s", _PATH_DEV); 122 if (unveil(_PATH_DEVDB, "r") == -1) 123 err(1, "unveil %s", _PATH_DEVDB); 124 if (pledge("stdio rpath wpath getpw proc", NULL) == -1) 125 err(1, "pledge"); 126 127 if (!(fp = fopen(_PATH_UTMP, "r"))) 128 err(1, "cannot read %s", _PATH_UTMP); 129 iov.iov_base = mbuf; 130 iov.iov_len = mbufsize; 131 132 while (fread(&utmp, sizeof(utmp), 1, fp) == 1) { 133 if (!utmp.ut_name[0]) 134 continue; 135 if (grouplist) { 136 ingroup = 0; 137 strncpy(username, utmp.ut_name, sizeof(utmp.ut_name)); 138 username[sizeof(utmp.ut_name)] = '\0'; 139 pw = getpwnam(username); 140 if (!pw) 141 continue; 142 for (g = grouplist; g && ingroup == 0; g = g->next) { 143 if (g->gid == pw->pw_gid) 144 ingroup = 1; 145 for (mem = g->mem; *mem && ingroup == 0; mem++) 146 if (strcmp(username, *mem) == 0) 147 ingroup = 1; 148 } 149 if (ingroup == 0) 150 continue; 151 } 152 strncpy(line, utmp.ut_line, sizeof(utmp.ut_line)); 153 line[sizeof(utmp.ut_line)] = '\0'; 154 un = malloc(sizeof(struct utmptty)); 155 if (un == NULL) 156 err(1, "malloc"); 157 un->tty = strndup(line, sizeof(utmp.ut_line)); 158 if (un->tty == NULL) 159 err(1, "strndup"); 160 SLIST_INSERT_HEAD(&utmphead, un, next); 161 } 162 fclose(fp); 163 164 if (pledge("stdio rpath wpath proc", NULL) == -1) 165 err(1, "pledge"); 166 167 SLIST_FOREACH(un, &utmphead, next) { 168 if ((p = ttymsg(&iov, 1, un->tty, 60*5)) != NULL) 169 warnx("%s", p); 170 } 171 172 exit(0); 173 } 174 175 void 176 makemsg(char *fname) 177 { 178 int cnt; 179 struct tm *lt; 180 struct passwd *pw; 181 struct stat sbuf; 182 time_t now; 183 FILE *fp; 184 int fd; 185 char *p, *whom, hostname[HOST_NAME_MAX+1], lbuf[100], tmpname[PATH_MAX]; 186 char *ttynam; 187 unsigned char ch; 188 189 snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXXXXXX", _PATH_TMP); 190 if ((fd = mkstemp(tmpname)) >= 0) { 191 (void)unlink(tmpname); 192 fp = fdopen(fd, "r+"); 193 } 194 if (fd == -1 || fp == NULL) 195 err(1, "can't open temporary file"); 196 197 if (!nobanner) { 198 if (!(whom = getlogin())) 199 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 200 (void)gethostname(hostname, sizeof(hostname)); 201 (void)time(&now); 202 lt = localtime(&now); 203 if ((ttynam = ttyname(STDERR_FILENO)) == NULL) 204 ttynam = "(not a tty)"; 205 206 /* 207 * all this stuff is to blank out a square for the message; 208 * we wrap message lines at column 79, not 80, because some 209 * terminals wrap after 79, some do not, and we can't tell. 210 * Which means that we may leave a non-blank character 211 * in column 80, but that can't be helped. 212 */ 213 (void)fprintf(fp, "\r%79s\r\n", " "); 214 (void)snprintf(lbuf, sizeof lbuf, 215 "Broadcast Message from %s@%s", whom, hostname); 216 (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf); 217 (void)snprintf(lbuf, sizeof lbuf, 218 " (%s) at %d:%02d ...", ttynam, 219 lt->tm_hour, lt->tm_min); 220 (void)fprintf(fp, "%-79.79s\r\n", lbuf); 221 } 222 (void)fprintf(fp, "%79s\r\n", " "); 223 224 if (fname) { 225 gid_t egid = getegid(); 226 227 setegid(getgid()); 228 if (freopen(fname, "r", stdin) == NULL) 229 err(1, "can't read %s", fname); 230 setegid(egid); 231 } 232 while (fgets(lbuf, sizeof(lbuf), stdin)) 233 for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) { 234 if (cnt == 79 || ch == '\n') { 235 for (; cnt < 79; ++cnt) 236 putc(' ', fp); 237 putc('\r', fp); 238 putc('\n', fp); 239 cnt = -1; 240 } else if (!isu8cont(ch)) 241 putc(isprint(ch) || isspace(ch) || ch == '\a' ? 242 ch : '?', fp); 243 } 244 (void)fprintf(fp, "%79s\r\n", " "); 245 rewind(fp); 246 247 if (fstat(fd, &sbuf)) 248 err(1, "can't stat temporary file"); 249 mbufsize = sbuf.st_size; 250 mbuf = malloc((u_int)mbufsize); 251 if (mbuf == NULL) 252 err(1, NULL); 253 if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) 254 err(1, "can't read temporary file"); 255 (void)close(fd); 256 } 257 258 void 259 addgroup(struct group *grp, char *name) 260 { 261 int i; 262 struct wallgroup *g; 263 264 for (i = 0; grp->gr_mem[i]; i++) 265 ; 266 267 g = malloc(sizeof *g); 268 if (g == NULL) 269 err(1, NULL); 270 g->gid = grp->gr_gid; 271 g->name = name; 272 g->mem = calloc(i + 1, sizeof(char *)); 273 if (g->mem == NULL) 274 err(1, NULL); 275 for (i = 0; grp->gr_mem[i] != NULL; i++) { 276 g->mem[i] = strdup(grp->gr_mem[i]); 277 if (g->mem[i] == NULL) 278 err(1, NULL); 279 } 280 g->mem[i] = NULL; 281 g->next = grouplist; 282 grouplist = g; 283 } 284 285 void 286 usage(void) 287 { 288 extern char *__progname; 289 290 (void)fprintf(stderr, "usage: %s [-g group] [file]\n", __progname); 291 exit(1); 292 } 293 294 static int 295 isu8cont(unsigned char c) 296 { 297 return (c & (0x80 | 0x40)) == 0x80; 298 } 299