1 /* $OpenBSD: wall.c,v 1.25 2009/10/27 23:59:49 deraadt 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/param.h> 39 #include <sys/stat.h> 40 #include <sys/time.h> 41 #include <sys/uio.h> 42 43 #include <paths.h> 44 #include <pwd.h> 45 #include <grp.h> 46 #include <stdio.h> 47 #include <stdlib.h> 48 #include <string.h> 49 #include <unistd.h> 50 #include <utmp.h> 51 #include <vis.h> 52 #include <err.h> 53 54 struct wallgroup { 55 gid_t gid; 56 char *name; 57 char **mem; 58 struct wallgroup *next; 59 } *grouplist; 60 61 void makemsg(char *); 62 void addgroup(struct group *, char *); 63 char *ttymsg(struct iovec *, int, char *, int); 64 __dead void usage(void); 65 66 int nobanner; 67 int mbufsize; 68 char *mbuf; 69 70 /* ARGSUSED */ 71 int 72 main(int argc, char **argv) 73 { 74 FILE *fp; 75 int ch, ingroup; 76 struct iovec iov; 77 struct utmp utmp; 78 char *p, **mem; 79 char line[sizeof(utmp.ut_line) + 1]; 80 char username[sizeof(utmp.ut_name) + 1]; 81 struct passwd *pw; 82 struct group *grp; 83 struct wallgroup *g; 84 85 while ((ch = getopt(argc, argv, "ng:")) != -1) 86 switch (ch) { 87 case 'n': 88 /* undoc option for shutdown: suppress banner */ 89 pw = getpwnam("nobody"); 90 if (geteuid() == 0 || (pw && getuid() == pw->pw_uid)) 91 nobanner = 1; 92 break; 93 case 'g': 94 if ((grp = getgrnam(optarg)) == NULL) 95 errx(1, "unknown group `%s'", optarg); 96 addgroup(grp, optarg); 97 break; 98 default: 99 usage(); 100 } 101 argc -= optind; 102 argv += optind; 103 if (argc > 1) 104 usage(); 105 106 makemsg(*argv); 107 108 if (!(fp = fopen(_PATH_UTMP, "r"))) 109 err(1, "cannot read %s", _PATH_UTMP); 110 iov.iov_base = mbuf; 111 iov.iov_len = mbufsize; 112 /* NOSTRICT */ 113 while (fread(&utmp, sizeof(utmp), 1, fp) == 1) { 114 if (!utmp.ut_name[0]) 115 continue; 116 if (grouplist) { 117 ingroup = 0; 118 strncpy(username, utmp.ut_name, sizeof(utmp.ut_name)); 119 username[sizeof(utmp.ut_name)] = '\0'; 120 pw = getpwnam(username); 121 if (!pw) 122 continue; 123 for (g = grouplist; g && ingroup == 0; g = g->next) { 124 if (g->gid == pw->pw_gid) 125 ingroup = 1; 126 for (mem = g->mem; *mem && ingroup == 0; mem++) 127 if (strcmp(username, *mem) == 0) 128 ingroup = 1; 129 } 130 if (ingroup == 0) 131 continue; 132 } 133 strncpy(line, utmp.ut_line, sizeof(utmp.ut_line)); 134 line[sizeof(utmp.ut_line)] = '\0'; 135 if ((p = ttymsg(&iov, 1, line, 60*5)) != NULL) 136 warnx("%s", p); 137 } 138 exit(0); 139 } 140 141 void 142 makemsg(char *fname) 143 { 144 int ch, cnt; 145 struct tm *lt; 146 struct passwd *pw; 147 struct stat sbuf; 148 time_t now; 149 FILE *fp; 150 int fd; 151 char *p, *whom, hostname[MAXHOSTNAMELEN], lbuf[100], tmpname[MAXPATHLEN]; 152 char tmpbuf[5]; 153 char *ttynam; 154 155 snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXXXXXX", _PATH_TMP); 156 if ((fd = mkstemp(tmpname)) >= 0) { 157 (void)unlink(tmpname); 158 fp = fdopen(fd, "r+"); 159 } 160 if (fd == -1 || fp == NULL) 161 err(1, "can't open temporary file"); 162 163 if (!nobanner) { 164 if (!(whom = getlogin())) 165 whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 166 (void)gethostname(hostname, sizeof(hostname)); 167 (void)time(&now); 168 lt = localtime(&now); 169 if ((ttynam = ttyname(STDERR_FILENO)) == NULL) 170 ttynam = "(not a tty)"; 171 172 /* 173 * all this stuff is to blank out a square for the message; 174 * we wrap message lines at column 79, not 80, because some 175 * terminals wrap after 79, some do not, and we can't tell. 176 * Which means that we may leave a non-blank character 177 * in column 80, but that can't be helped. 178 */ 179 (void)fprintf(fp, "\r%79s\r\n", " "); 180 (void)snprintf(lbuf, sizeof lbuf, 181 "Broadcast Message from %s@%s", whom, hostname); 182 (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf); 183 (void)snprintf(lbuf, sizeof lbuf, 184 " (%s) at %d:%02d ...", ttynam, 185 lt->tm_hour, lt->tm_min); 186 (void)fprintf(fp, "%-79.79s\r\n", lbuf); 187 } 188 (void)fprintf(fp, "%79s\r\n", " "); 189 190 if (fname) { 191 gid_t egid = getegid(); 192 193 setegid(getgid()); 194 if (freopen(fname, "r", stdin) == NULL) 195 err(1, "can't read %s", fname); 196 setegid(egid); 197 } 198 while (fgets(lbuf, sizeof(lbuf), stdin)) 199 for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) { 200 vis(tmpbuf, ch, VIS_SAFE|VIS_NOSLASH, p[1]); 201 if (cnt == 79+1-strlen(tmpbuf) || ch == '\n') { 202 for (; cnt < 79+1-strlen(tmpbuf); ++cnt) 203 putc(' ', fp); 204 putc('\r', fp); 205 putc('\n', fp); 206 cnt = -1; 207 } 208 if (ch != '\n') { 209 int xx; 210 211 for (xx = 0; tmpbuf[xx]; xx++) 212 putc(tmpbuf[xx], fp); 213 } 214 } 215 (void)fprintf(fp, "%79s\r\n", " "); 216 rewind(fp); 217 218 if (fstat(fd, &sbuf)) 219 err(1, "can't stat temporary file"); 220 mbufsize = sbuf.st_size; 221 mbuf = malloc((u_int)mbufsize); 222 if (mbuf == NULL) 223 err(1, NULL); 224 if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) 225 err(1, "can't read temporary file"); 226 (void)close(fd); 227 } 228 229 void 230 addgroup(struct group *grp, char *name) 231 { 232 int i; 233 struct wallgroup *g; 234 235 for (i = 0; grp->gr_mem[i]; i++) 236 ; 237 238 g = (struct wallgroup *)malloc(sizeof *g); 239 if (g == NULL) 240 err(1, NULL); 241 g->gid = grp->gr_gid; 242 g->name = name; 243 g->mem = (char **)calloc(i + 1, sizeof(char *)); 244 if (g->mem == NULL) 245 err(1, NULL); 246 for (i = 0; grp->gr_mem[i] != NULL; i++) { 247 g->mem[i] = strdup(grp->gr_mem[i]); 248 if (g->mem[i] == NULL) 249 err(1, NULL); 250 } 251 g->mem[i] = NULL; 252 g->next = grouplist; 253 grouplist = g; 254 } 255 256 void 257 usage(void) 258 { 259 extern char *__progname; 260 261 (void)fprintf(stderr, "usage: %s [-g group] [file]\n", __progname); 262 exit(1); 263 } 264