1 /* $OpenBSD: wall.c,v 1.37 2023/03/08 04:43:12 guenther 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 int 78 main(int argc, char **argv) 79 { 80 FILE *fp; 81 int ch, ingroup; 82 struct iovec iov; 83 struct utmp utmp; 84 char *p, **mem; 85 char line[sizeof(utmp.ut_line) + 1]; 86 char username[sizeof(utmp.ut_name) + 1]; 87 struct passwd *pw; 88 struct group *grp; 89 struct wallgroup *g; 90 struct utmptty *un; 91 SLIST_HEAD(,utmptty) utmphead; 92 SLIST_INIT(&utmphead); 93 94 while ((ch = getopt(argc, argv, "ng:")) != -1) 95 switch (ch) { 96 case 'n': 97 /* undoc option for shutdown: suppress banner */ 98 pw = getpwnam("nobody"); 99 if (geteuid() == 0 || (pw && getuid() == pw->pw_uid)) 100 nobanner = 1; 101 break; 102 case 'g': 103 if ((grp = getgrnam(optarg)) == NULL) 104 errx(1, "unknown group `%s'", optarg); 105 addgroup(grp, optarg); 106 break; 107 default: 108 usage(); 109 } 110 argc -= optind; 111 argv += optind; 112 if (argc > 1) 113 usage(); 114 115 makemsg(*argv); 116 117 if (unveil(_PATH_UTMP, "r") == -1) 118 err(1, "unveil %s", _PATH_UTMP); 119 if (unveil(_PATH_DEV, "w") == -1) 120 err(1, "unveil %s", _PATH_DEV); 121 if (unveil(_PATH_DEVDB, "r") == -1) 122 err(1, "unveil %s", _PATH_DEVDB); 123 if (pledge("stdio rpath wpath getpw proc", NULL) == -1) 124 err(1, "pledge"); 125 126 if (!(fp = fopen(_PATH_UTMP, "r"))) 127 err(1, "cannot read %s", _PATH_UTMP); 128 iov.iov_base = mbuf; 129 iov.iov_len = mbufsize; 130 131 while (fread(&utmp, sizeof(utmp), 1, fp) == 1) { 132 if (!utmp.ut_name[0]) 133 continue; 134 if (grouplist) { 135 ingroup = 0; 136 strncpy(username, utmp.ut_name, sizeof(utmp.ut_name)); 137 username[sizeof(utmp.ut_name)] = '\0'; 138 pw = getpwnam(username); 139 if (!pw) 140 continue; 141 for (g = grouplist; g && ingroup == 0; g = g->next) { 142 if (g->gid == pw->pw_gid) 143 ingroup = 1; 144 for (mem = g->mem; *mem && ingroup == 0; mem++) 145 if (strcmp(username, *mem) == 0) 146 ingroup = 1; 147 } 148 if (ingroup == 0) 149 continue; 150 } 151 strncpy(line, utmp.ut_line, sizeof(utmp.ut_line)); 152 line[sizeof(utmp.ut_line)] = '\0'; 153 un = malloc(sizeof(struct utmptty)); 154 if (un == NULL) 155 err(1, "malloc"); 156 un->tty = strndup(line, sizeof(utmp.ut_line)); 157 if (un->tty == NULL) 158 err(1, "strndup"); 159 SLIST_INSERT_HEAD(&utmphead, un, next); 160 } 161 fclose(fp); 162 163 if (pledge("stdio rpath wpath proc", NULL) == -1) 164 err(1, "pledge"); 165 166 SLIST_FOREACH(un, &utmphead, next) { 167 if ((p = ttymsg(&iov, 1, un->tty, 60*5)) != NULL) 168 warnx("%s", p); 169 } 170 171 exit(0); 172 } 173 174 void 175 makemsg(char *fname) 176 { 177 int cnt; 178 struct tm *lt; 179 struct passwd *pw; 180 struct stat sbuf; 181 time_t now; 182 FILE *fp; 183 int fd; 184 char *p, *whom, hostname[HOST_NAME_MAX+1], lbuf[100], tmpname[PATH_MAX]; 185 char *ttynam; 186 unsigned char ch; 187 188 snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXXXXXX", _PATH_TMP); 189 if ((fd = mkstemp(tmpname)) >= 0) { 190 (void)unlink(tmpname); 191 fp = fdopen(fd, "r+"); 192 } 193 if (fd == -1 || fp == NULL) 194 err(1, "can't open temporary file"); 195 196 if (!nobanner) { 197 if (!(whom = getlogin())) 198 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 199 (void)gethostname(hostname, sizeof(hostname)); 200 (void)time(&now); 201 lt = localtime(&now); 202 if ((ttynam = ttyname(STDERR_FILENO)) == NULL) 203 ttynam = "(not a tty)"; 204 205 /* 206 * all this stuff is to blank out a square for the message; 207 * we wrap message lines at column 79, not 80, because some 208 * terminals wrap after 79, some do not, and we can't tell. 209 * Which means that we may leave a non-blank character 210 * in column 80, but that can't be helped. 211 */ 212 (void)fprintf(fp, "\r%79s\r\n", " "); 213 (void)snprintf(lbuf, sizeof lbuf, 214 "Broadcast Message from %s@%s", whom, hostname); 215 (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf); 216 (void)snprintf(lbuf, sizeof lbuf, 217 " (%s) at %d:%02d ...", ttynam, 218 lt->tm_hour, lt->tm_min); 219 (void)fprintf(fp, "%-79.79s\r\n", lbuf); 220 } 221 (void)fprintf(fp, "%79s\r\n", " "); 222 223 if (fname) { 224 gid_t egid = getegid(); 225 226 setegid(getgid()); 227 if (freopen(fname, "r", stdin) == NULL) 228 err(1, "can't read %s", fname); 229 setegid(egid); 230 } 231 cnt = 0; 232 while (fgets(lbuf, sizeof(lbuf), stdin)) 233 for (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 if (ch != '\n') 241 p--; 242 } else if (!isu8cont(ch)) 243 putc(isprint(ch) || isspace(ch) || ch == '\a' ? 244 ch : '?', fp); 245 } 246 (void)fprintf(fp, "%79s\r\n", " "); 247 rewind(fp); 248 249 if (fstat(fd, &sbuf)) 250 err(1, "can't stat temporary file"); 251 mbufsize = sbuf.st_size; 252 mbuf = malloc((u_int)mbufsize); 253 if (mbuf == NULL) 254 err(1, NULL); 255 if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) 256 err(1, "can't read temporary file"); 257 (void)close(fd); 258 } 259 260 void 261 addgroup(struct group *grp, char *name) 262 { 263 int i; 264 struct wallgroup *g; 265 266 for (i = 0; grp->gr_mem[i]; i++) 267 ; 268 269 g = malloc(sizeof *g); 270 if (g == NULL) 271 err(1, NULL); 272 g->gid = grp->gr_gid; 273 g->name = name; 274 g->mem = calloc(i + 1, sizeof(char *)); 275 if (g->mem == NULL) 276 err(1, NULL); 277 for (i = 0; grp->gr_mem[i] != NULL; i++) { 278 g->mem[i] = strdup(grp->gr_mem[i]); 279 if (g->mem[i] == NULL) 280 err(1, NULL); 281 } 282 g->mem[i] = NULL; 283 g->next = grouplist; 284 grouplist = g; 285 } 286 287 void 288 usage(void) 289 { 290 extern char *__progname; 291 292 (void)fprintf(stderr, "usage: %s [-g group] [file]\n", __progname); 293 exit(1); 294 } 295 296 static int 297 isu8cont(unsigned char c) 298 { 299 return (c & (0x80 | 0x40)) == 0x80; 300 } 301