wall.c revision 227200
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 * 4. Neither the name of the University nor the names of its contributors 141590Srgrimes * may be used to endorse or promote products derived from this software 151590Srgrimes * without specific prior written permission. 161590Srgrimes * 171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201590Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271590Srgrimes * SUCH DAMAGE. 281590Srgrimes */ 291590Srgrimes 3087675Smarkm#include <sys/cdefs.h> 3187675Smarkm 3287675Smarkm__FBSDID("$FreeBSD: head/usr.bin/wall/wall.c 227200 2011-11-06 08:18:55Z ed $"); 3387675Smarkm 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"; 3887675Smarkm#endif 391590Srgrimes 401590Srgrimes#ifndef lint 4187675Smarkmstatic const char sccsid[] = "@(#)wall.c 8.2 (Berkeley) 11/16/93"; 4228695Scharnier#endif 431590Srgrimes 441590Srgrimes/* 451590Srgrimes * This program is not related to David Wall, whose Stanford Ph.D. thesis 461590Srgrimes * is entitled "Mechanisms for Broadcast and Selective Broadcast". 471590Srgrimes */ 481590Srgrimes 491590Srgrimes#include <sys/param.h> 501590Srgrimes#include <sys/stat.h> 511590Srgrimes#include <sys/uio.h> 521590Srgrimes 5328695Scharnier#include <ctype.h> 5428695Scharnier#include <err.h> 5573255Simp#include <grp.h> 5625777Sache#include <locale.h> 571590Srgrimes#include <paths.h> 581590Srgrimes#include <pwd.h> 591590Srgrimes#include <stdio.h> 601590Srgrimes#include <stdlib.h> 611590Srgrimes#include <string.h> 6229434Sache#include <time.h> 631590Srgrimes#include <unistd.h> 64202200Sed#include <utmpx.h> 651590Srgrimes 6683242Sdd#include "ttymsg.h" 6783242Sdd 6873255Simpstatic void makemsg(char *); 6973255Simpstatic void usage(void); 701590Srgrimes 71227200Sedstatic struct wallgroup { 7273255Simp struct wallgroup *next; 7373255Simp char *name; 7473255Simp gid_t gid; 7573255Simp} *grouplist; 76227200Sedstatic int nobanner; 77227200Sedstatic int mbufsize; 78227200Sedstatic char *mbuf; 791590Srgrimes 80155875Scognetstatic int 81200156Sedttystat(char *line) 82155875Scognet{ 83155875Scognet struct stat sb; 84155875Scognet char ttybuf[MAXPATHLEN]; 85155875Scognet 86200156Sed (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); 87155875Scognet if (stat(ttybuf, &sb) == 0) { 88155875Scognet return (0); 89155875Scognet } else 90155875Scognet return (-1); 91155875Scognet} 92155875Scognet 931590Srgrimesint 9473255Simpmain(int argc, char *argv[]) 951590Srgrimes{ 961590Srgrimes struct iovec iov; 97200156Sed struct utmpx *utmp; 9873255Simp int ch; 9983082Sru int ingroup; 10073255Simp struct wallgroup *g; 10173255Simp struct group *grp; 10283242Sdd char **np; 10383242Sdd const char *p; 10473255Simp struct passwd *pw; 1051590Srgrimes 10625777Sache (void)setlocale(LC_CTYPE, ""); 10725777Sache 10873255Simp while ((ch = getopt(argc, argv, "g:n")) != -1) 1091590Srgrimes switch (ch) { 1101590Srgrimes case 'n': 1111590Srgrimes /* undoc option for shutdown: suppress banner */ 1121590Srgrimes if (geteuid() == 0) 1131590Srgrimes nobanner = 1; 1141590Srgrimes break; 11573255Simp case 'g': 11673255Simp g = (struct wallgroup *)malloc(sizeof *g); 11773255Simp g->next = grouplist; 11873255Simp g->name = optarg; 11973255Simp g->gid = -1; 12073255Simp grouplist = g; 12173255Simp break; 1221590Srgrimes case '?': 1231590Srgrimes default: 12428695Scharnier usage(); 1251590Srgrimes } 1261590Srgrimes argc -= optind; 1271590Srgrimes argv += optind; 1281590Srgrimes if (argc > 1) 12928695Scharnier usage(); 1301590Srgrimes 13173255Simp for (g = grouplist; g; g = g->next) { 13273255Simp grp = getgrnam(g->name); 13373320Simp if (grp != NULL) 13473255Simp g->gid = grp->gr_gid; 13573320Simp else 13673320Simp warnx("%s: no such group", g->name); 13773255Simp } 13873255Simp 1391590Srgrimes makemsg(*argv); 1401590Srgrimes 1411590Srgrimes iov.iov_base = mbuf; 1421590Srgrimes iov.iov_len = mbufsize; 1431590Srgrimes /* NOSTRICT */ 144200156Sed while ((utmp = getutxent()) != NULL) { 145200156Sed if (utmp->ut_type != USER_PROCESS) 1461590Srgrimes continue; 147200156Sed if (ttystat(utmp->ut_line) != 0) 148155875Scognet continue; 14973255Simp if (grouplist) { 15083082Sru ingroup = 0; 151200156Sed pw = getpwnam(utmp->ut_user); 15273255Simp if (!pw) 15373255Simp continue; 15473255Simp for (g = grouplist; g && ingroup == 0; g = g->next) { 15587675Smarkm if (g->gid == (gid_t)-1) 15673255Simp continue; 15773255Simp if (g->gid == pw->pw_gid) 15873255Simp ingroup = 1; 15983082Sru else if ((grp = getgrgid(g->gid)) != NULL) { 16083082Sru for (np = grp->gr_mem; *np; np++) { 161200156Sed if (strcmp(*np, utmp->ut_user) == 0) { 16283082Sru ingroup = 1; 16383082Sru break; 16483082Sru } 16583082Sru } 16683082Sru } 16773255Simp } 16873255Simp if (ingroup == 0) 16973255Simp continue; 17073255Simp } 171200156Sed if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5)) != NULL) 17228695Scharnier warnx("%s", p); 1731590Srgrimes } 1741590Srgrimes exit(0); 1751590Srgrimes} 1761590Srgrimes 17728695Scharnierstatic void 178201224Sedusage(void) 17928695Scharnier{ 18073320Simp (void)fprintf(stderr, "usage: wall [-g group] [file]\n"); 18128695Scharnier exit(1); 18228695Scharnier} 18328695Scharnier 1841590Srgrimesvoid 18573255Simpmakemsg(char *fname) 1861590Srgrimes{ 18773255Simp int cnt; 18873255Simp unsigned char ch; 1891590Srgrimes struct tm *lt; 1901590Srgrimes struct passwd *pw; 1911590Srgrimes struct stat sbuf; 19229434Sache time_t now; 1931590Srgrimes FILE *fp; 1941590Srgrimes int fd; 19587675Smarkm char *p, hostname[MAXHOSTNAMELEN], lbuf[256], tmpname[64]; 19687675Smarkm const char *tty; 19769231Skris const char *whom; 19876367Skris gid_t egid; 1991590Srgrimes 20069231Skris (void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP); 20169231Skris if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+"))) 20269231Skris err(1, "can't open temporary file"); 2031590Srgrimes (void)unlink(tmpname); 2041590Srgrimes 2051590Srgrimes if (!nobanner) { 20669231Skris tty = ttyname(STDERR_FILENO); 20769231Skris if (tty == NULL) 20866557Sn_hibma tty = "no tty"; 20966557Sn_hibma 2101590Srgrimes if (!(whom = getlogin())) 2111590Srgrimes whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 2121590Srgrimes (void)gethostname(hostname, sizeof(hostname)); 2131590Srgrimes (void)time(&now); 2141590Srgrimes lt = localtime(&now); 2151590Srgrimes 2161590Srgrimes /* 2171590Srgrimes * all this stuff is to blank out a square for the message; 2181590Srgrimes * we wrap message lines at column 79, not 80, because some 2191590Srgrimes * terminals wrap after 79, some do not, and we can't tell. 2201590Srgrimes * Which means that we may leave a non-blank character 2211590Srgrimes * in column 80, but that can't be helped. 2221590Srgrimes */ 2231590Srgrimes (void)fprintf(fp, "\r%79s\r\n", " "); 22441717Sdillon (void)snprintf(lbuf, sizeof(lbuf), 22541717Sdillon "Broadcast Message from %s@%s", 2261590Srgrimes whom, hostname); 2271590Srgrimes (void)fprintf(fp, "%-79.79s\007\007\r\n", lbuf); 22869231Skris (void)snprintf(lbuf, sizeof(lbuf), 22966557Sn_hibma " (%s) at %d:%02d %s...", tty, 23063909Sasmodai lt->tm_hour, lt->tm_min, lt->tm_zone); 2311590Srgrimes (void)fprintf(fp, "%-79.79s\r\n", lbuf); 2321590Srgrimes } 2331590Srgrimes (void)fprintf(fp, "%79s\r\n", " "); 2341590Srgrimes 23576367Skris if (fname) { 23676367Skris egid = getegid(); 23776367Skris setegid(getgid()); 23876367Skris if (freopen(fname, "r", stdin) == NULL) 23976367Skris err(1, "can't read %s", fname); 24076367Skris setegid(egid); 24176367Skris } 242223940Sobrien cnt = 0; 243175346Sdas while (fgets(lbuf, sizeof(lbuf), stdin)) { 244223940Sobrien for (p = lbuf; (ch = *p) != '\0'; ++p, ++cnt) { 24550776Sdbaker if (ch == '\r') { 246175346Sdas putc('\r', fp); 24750776Sdbaker cnt = 0; 248175346Sdas continue; 249175346Sdas } else if (ch == '\n') { 2501590Srgrimes for (; cnt < 79; ++cnt) 2511590Srgrimes putc(' ', fp); 2521590Srgrimes putc('\r', fp); 2531590Srgrimes putc('\n', fp); 254175346Sdas break; 255175346Sdas } 256175346Sdas if (cnt == 79) { 257175346Sdas putc('\r', fp); 258175346Sdas putc('\n', fp); 2591590Srgrimes cnt = 0; 260175346Sdas } 261175346Sdas if (((ch & 0x80) && ch < 0xA0) || 26229434Sache /* disable upper controls */ 26329434Sache (!isprint(ch) && !isspace(ch) && 26429434Sache ch != '\a' && ch != '\b') 26524729Sache ) { 26624729Sache if (ch & 0x80) { 26724729Sache ch &= 0x7F; 26829434Sache putc('M', fp); 26929434Sache if (++cnt == 79) { 27029434Sache putc('\r', fp); 27129434Sache putc('\n', fp); 27229434Sache cnt = 0; 27329434Sache } 27429434Sache putc('-', fp); 27529434Sache if (++cnt == 79) { 27629434Sache putc('\r', fp); 27729434Sache putc('\n', fp); 27829434Sache cnt = 0; 27929434Sache } 28024729Sache } 28129434Sache if (iscntrl(ch)) { 28229434Sache ch ^= 040; 28329434Sache putc('^', fp); 28429434Sache if (++cnt == 79) { 28529434Sache putc('\r', fp); 28629434Sache putc('\n', fp); 28729434Sache cnt = 0; 28829434Sache } 28929434Sache } 29029434Sache } 291175346Sdas putc(ch, fp); 2921590Srgrimes } 293175346Sdas } 2941590Srgrimes (void)fprintf(fp, "%79s\r\n", " "); 2951590Srgrimes rewind(fp); 2961590Srgrimes 29728695Scharnier if (fstat(fd, &sbuf)) 29869231Skris err(1, "can't stat temporary file"); 2991590Srgrimes mbufsize = sbuf.st_size; 30028695Scharnier if (!(mbuf = malloc((u_int)mbufsize))) 30169231Skris err(1, "out of memory"); 30287675Smarkm if ((int)fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) 30369231Skris err(1, "can't read temporary file"); 3041590Srgrimes (void)close(fd); 3051590Srgrimes} 306