wall.c revision 76367
11590Srgrimes/* 21590Srgrimes * Copyright (c) 1988, 1990, 1993 31590Srgrimes * The Regents of the University of California. All rights reserved. 41590Srgrimes * 51590Srgrimes * Redistribution and use in source and binary forms, with or without 61590Srgrimes * modification, are permitted provided that the following conditions 71590Srgrimes * are met: 81590Srgrimes * 1. Redistributions of source code must retain the above copyright 91590Srgrimes * notice, this list of conditions and the following disclaimer. 101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111590Srgrimes * notice, this list of conditions and the following disclaimer in the 121590Srgrimes * documentation and/or other materials provided with the distribution. 131590Srgrimes * 3. All advertising materials mentioning features or use of this software 141590Srgrimes * must display the following acknowledgement: 151590Srgrimes * This product includes software developed by the University of 161590Srgrimes * California, Berkeley and its contributors. 171590Srgrimes * 4. Neither the name of the University nor the names of its contributors 181590Srgrimes * may be used to endorse or promote products derived from this software 191590Srgrimes * without specific prior written permission. 201590Srgrimes * 211590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 221590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 231590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 241590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 251590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 261590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 271590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 281590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 291590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 301590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 311590Srgrimes * SUCH DAMAGE. 321590Srgrimes */ 331590Srgrimes 341590Srgrimes#ifndef lint 3528695Scharnierstatic const char copyright[] = 361590Srgrimes"@(#) Copyright (c) 1988, 1990, 1993\n\ 371590Srgrimes The Regents of the University of California. All rights reserved.\n"; 381590Srgrimes#endif /* not lint */ 391590Srgrimes 401590Srgrimes#ifndef lint 4128695Scharnier#if 0 421590Srgrimesstatic char sccsid[] = "@(#)wall.c 8.2 (Berkeley) 11/16/93"; 4328695Scharnier#endif 4428695Scharnierstatic const char rcsid[] = 4550477Speter "$FreeBSD: head/usr.bin/wall/wall.c 76367 2001-05-08 11:11:42Z kris $"; 461590Srgrimes#endif /* not lint */ 471590Srgrimes 481590Srgrimes/* 491590Srgrimes * This program is not related to David Wall, whose Stanford Ph.D. thesis 501590Srgrimes * is entitled "Mechanisms for Broadcast and Selective Broadcast". 511590Srgrimes */ 521590Srgrimes 531590Srgrimes#include <sys/param.h> 541590Srgrimes#include <sys/stat.h> 551590Srgrimes#include <sys/uio.h> 561590Srgrimes 5728695Scharnier#include <ctype.h> 5828695Scharnier#include <err.h> 5973255Simp#include <grp.h> 6025777Sache#include <locale.h> 611590Srgrimes#include <paths.h> 621590Srgrimes#include <pwd.h> 631590Srgrimes#include <stdio.h> 641590Srgrimes#include <stdlib.h> 651590Srgrimes#include <string.h> 6629434Sache#include <time.h> 671590Srgrimes#include <unistd.h> 681590Srgrimes#include <utmp.h> 691590Srgrimes 7073255Simpstatic void makemsg(char *); 7173255Simpstatic void usage(void); 7273255Simpchar *ttymsg(struct iovec *, int, const char *, int); 731590Srgrimes 741590Srgrimes#define IGNOREUSER "sleeper" 751590Srgrimes 7673255Simpstruct wallgroup { 7773255Simp struct wallgroup *next; 7873255Simp char *name; 7973255Simp gid_t gid; 8073255Simp} *grouplist; 811590Srgrimesint nobanner; 821590Srgrimesint mbufsize; 831590Srgrimeschar *mbuf; 841590Srgrimes 851590Srgrimesint 8673255Simpmain(int argc, char *argv[]) 871590Srgrimes{ 881590Srgrimes struct iovec iov; 891590Srgrimes struct utmp utmp; 9073255Simp gid_t grps[NGROUPS_MAX]; 9173255Simp int ch; 9273255Simp int ingroup, ngrps, i; 931590Srgrimes FILE *fp; 9473255Simp struct wallgroup *g; 9573255Simp struct group *grp; 9669231Skris char *p; 9773255Simp struct passwd *pw; 981590Srgrimes char line[sizeof(utmp.ut_line) + 1]; 9973255Simp char username[sizeof(utmp.ut_name) + 1]; 1001590Srgrimes 10173255Simp ingroup = 0; 10225777Sache (void)setlocale(LC_CTYPE, ""); 10325777Sache 10473255Simp while ((ch = getopt(argc, argv, "g:n")) != -1) 1051590Srgrimes switch (ch) { 1061590Srgrimes case 'n': 1071590Srgrimes /* undoc option for shutdown: suppress banner */ 1081590Srgrimes if (geteuid() == 0) 1091590Srgrimes nobanner = 1; 1101590Srgrimes break; 11173255Simp case 'g': 11273255Simp g = (struct wallgroup *)malloc(sizeof *g); 11373255Simp g->next = grouplist; 11473255Simp g->name = optarg; 11573255Simp g->gid = -1; 11673255Simp grouplist = g; 11773255Simp break; 1181590Srgrimes case '?': 1191590Srgrimes default: 12028695Scharnier usage(); 1211590Srgrimes } 1221590Srgrimes argc -= optind; 1231590Srgrimes argv += optind; 1241590Srgrimes if (argc > 1) 12528695Scharnier usage(); 1261590Srgrimes 12773255Simp for (g = grouplist; g; g = g->next) { 12873255Simp grp = getgrnam(g->name); 12973320Simp if (grp != NULL) 13073255Simp g->gid = grp->gr_gid; 13173320Simp else 13273320Simp warnx("%s: no such group", g->name); 13373255Simp } 13473255Simp 1351590Srgrimes makemsg(*argv); 1361590Srgrimes 13728695Scharnier if (!(fp = fopen(_PATH_UTMP, "r"))) 13869231Skris err(1, "cannot read %s", _PATH_UTMP); 1391590Srgrimes iov.iov_base = mbuf; 1401590Srgrimes iov.iov_len = mbufsize; 1411590Srgrimes /* NOSTRICT */ 1421590Srgrimes while (fread((char *)&utmp, sizeof(utmp), 1, fp) == 1) { 1431590Srgrimes if (!utmp.ut_name[0] || 1441590Srgrimes !strncmp(utmp.ut_name, IGNOREUSER, sizeof(utmp.ut_name))) 1451590Srgrimes continue; 14673255Simp if (grouplist) { 14773255Simp strlcpy(username, utmp.ut_name, sizeof(utmp.ut_name)); 14873255Simp pw = getpwnam(username); 14973255Simp if (!pw) 15073255Simp continue; 15173255Simp ngrps = getgroups(pw->pw_gid, grps); 15273255Simp for (g = grouplist; g && ingroup == 0; g = g->next) { 15373255Simp if (g->gid == -1) 15473255Simp continue; 15573255Simp if (g->gid == pw->pw_gid) 15673255Simp ingroup = 1; 15773255Simp for (i = 0; i < ngrps && ingroup == 0; i++) 15873255Simp if (g->gid == grps[i]) 15973255Simp ingroup = 1; 16073255Simp } 16173255Simp if (ingroup == 0) 16273255Simp continue; 16373255Simp } 1641590Srgrimes strncpy(line, utmp.ut_line, sizeof(utmp.ut_line)); 1651590Srgrimes line[sizeof(utmp.ut_line)] = '\0'; 1661590Srgrimes if ((p = ttymsg(&iov, 1, line, 60*5)) != NULL) 16728695Scharnier warnx("%s", p); 1681590Srgrimes } 1691590Srgrimes exit(0); 1701590Srgrimes} 1711590Srgrimes 17228695Scharnierstatic void 17328695Scharnierusage() 17428695Scharnier{ 17573320Simp (void)fprintf(stderr, "usage: wall [-g group] [file]\n"); 17628695Scharnier exit(1); 17728695Scharnier} 17828695Scharnier 1791590Srgrimesvoid 18073255Simpmakemsg(char *fname) 1811590Srgrimes{ 18273255Simp int cnt; 18373255Simp unsigned char ch; 1841590Srgrimes struct tm *lt; 1851590Srgrimes struct passwd *pw; 1861590Srgrimes struct stat sbuf; 18729434Sache time_t now; 1881590Srgrimes FILE *fp; 1891590Srgrimes int fd; 19069231Skris char *p, *tty, hostname[MAXHOSTNAMELEN], lbuf[256], tmpname[64]; 19169231Skris const char *whom; 19276367Skris gid_t egid; 1931590Srgrimes 19469231Skris (void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP); 19569231Skris if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+"))) 19669231Skris err(1, "can't open temporary file"); 1971590Srgrimes (void)unlink(tmpname); 1981590Srgrimes 1991590Srgrimes if (!nobanner) { 20069231Skris tty = ttyname(STDERR_FILENO); 20169231Skris if (tty == NULL) 20266557Sn_hibma tty = "no tty"; 20366557Sn_hibma 2041590Srgrimes if (!(whom = getlogin())) 2051590Srgrimes whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 2061590Srgrimes (void)gethostname(hostname, sizeof(hostname)); 2071590Srgrimes (void)time(&now); 2081590Srgrimes lt = localtime(&now); 2091590Srgrimes 2101590Srgrimes /* 2111590Srgrimes * all this stuff is to blank out a square for the message; 2121590Srgrimes * we wrap message lines at column 79, not 80, because some 2131590Srgrimes * terminals wrap after 79, some do not, and we can't tell. 2141590Srgrimes * Which means that we may leave a non-blank character 2151590Srgrimes * in column 80, but that can't be helped. 2161590Srgrimes */ 2171590Srgrimes (void)fprintf(fp, "\r%79s\r\n", " "); 21841717Sdillon (void)snprintf(lbuf, sizeof(lbuf), 21941717Sdillon "Broadcast Message from %s@%s", 2201590Srgrimes whom, hostname); 2211590Srgrimes (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf); 22269231Skris (void)snprintf(lbuf, sizeof(lbuf), 22366557Sn_hibma " (%s) at %d:%02d %s...", tty, 22463909Sasmodai lt->tm_hour, lt->tm_min, lt->tm_zone); 2251590Srgrimes (void)fprintf(fp, "%-79.79s\r\n", lbuf); 2261590Srgrimes } 2271590Srgrimes (void)fprintf(fp, "%79s\r\n", " "); 2281590Srgrimes 22976367Skris if (fname) { 23076367Skris egid = getegid(); 23176367Skris setegid(getgid()); 23276367Skris if (freopen(fname, "r", stdin) == NULL) 23376367Skris err(1, "can't read %s", fname); 23476367Skris setegid(egid); 23576367Skris } 2361590Srgrimes while (fgets(lbuf, sizeof(lbuf), stdin)) 2371590Srgrimes for (cnt = 0, p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) { 23850776Sdbaker if (ch == '\r') { 23950776Sdbaker cnt = 0; 24050776Sdbaker } else if (cnt == 79 || ch == '\n') { 2411590Srgrimes for (; cnt < 79; ++cnt) 2421590Srgrimes putc(' ', fp); 2431590Srgrimes putc('\r', fp); 2441590Srgrimes putc('\n', fp); 2451590Srgrimes cnt = 0; 24629434Sache } else if (((ch & 0x80) && ch < 0xA0) || 24729434Sache /* disable upper controls */ 24829434Sache (!isprint(ch) && !isspace(ch) && 24929434Sache ch != '\a' && ch != '\b') 25024729Sache ) { 25124729Sache if (ch & 0x80) { 25224729Sache ch &= 0x7F; 25329434Sache putc('M', fp); 25429434Sache if (++cnt == 79) { 25529434Sache putc('\r', fp); 25629434Sache putc('\n', fp); 25729434Sache cnt = 0; 25829434Sache } 25929434Sache putc('-', fp); 26029434Sache if (++cnt == 79) { 26129434Sache putc('\r', fp); 26229434Sache putc('\n', fp); 26329434Sache cnt = 0; 26429434Sache } 26524729Sache } 26629434Sache if (iscntrl(ch)) { 26729434Sache ch ^= 040; 26829434Sache putc('^', fp); 26929434Sache if (++cnt == 79) { 27029434Sache putc('\r', fp); 27129434Sache putc('\n', fp); 27229434Sache cnt = 0; 27329434Sache } 27429434Sache } 27550776Sdbaker putc(ch, fp); 27650772Sdbaker } else { 27750772Sdbaker putc(ch, fp); 27829434Sache } 2791590Srgrimes } 2801590Srgrimes (void)fprintf(fp, "%79s\r\n", " "); 2811590Srgrimes rewind(fp); 2821590Srgrimes 28328695Scharnier if (fstat(fd, &sbuf)) 28469231Skris err(1, "can't stat temporary file"); 2851590Srgrimes mbufsize = sbuf.st_size; 28628695Scharnier if (!(mbuf = malloc((u_int)mbufsize))) 28769231Skris err(1, "out of memory"); 28828695Scharnier if (fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) 28969231Skris err(1, "can't read temporary file"); 2901590Srgrimes (void)close(fd); 2911590Srgrimes} 292