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