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