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$"); 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> 65233269Sglebius#include <wchar.h> 66233269Sglebius#include <wctype.h> 671590Srgrimes 6883242Sdd#include "ttymsg.h" 6983242Sdd 7073255Simpstatic void makemsg(char *); 7173255Simpstatic void usage(void); 721590Srgrimes 73227200Sedstatic struct wallgroup { 7473255Simp struct wallgroup *next; 7573255Simp char *name; 7673255Simp gid_t gid; 7773255Simp} *grouplist; 78227200Sedstatic int nobanner; 79227200Sedstatic int mbufsize; 80227200Sedstatic char *mbuf; 811590Srgrimes 82155875Scognetstatic int 83200156Sedttystat(char *line) 84155875Scognet{ 85155875Scognet struct stat sb; 86155875Scognet char ttybuf[MAXPATHLEN]; 87155875Scognet 88200156Sed (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); 89155875Scognet if (stat(ttybuf, &sb) == 0) { 90155875Scognet return (0); 91155875Scognet } else 92155875Scognet return (-1); 93155875Scognet} 94155875Scognet 951590Srgrimesint 9673255Simpmain(int argc, char *argv[]) 971590Srgrimes{ 981590Srgrimes struct iovec iov; 99200156Sed struct utmpx *utmp; 10073255Simp int ch; 10183082Sru int ingroup; 10273255Simp struct wallgroup *g; 10373255Simp struct group *grp; 10483242Sdd char **np; 10583242Sdd const char *p; 10673255Simp struct passwd *pw; 1071590Srgrimes 10825777Sache (void)setlocale(LC_CTYPE, ""); 10925777Sache 11073255Simp while ((ch = getopt(argc, argv, "g:n")) != -1) 1111590Srgrimes switch (ch) { 1121590Srgrimes case 'n': 1131590Srgrimes /* undoc option for shutdown: suppress banner */ 1141590Srgrimes if (geteuid() == 0) 1151590Srgrimes nobanner = 1; 1161590Srgrimes break; 11773255Simp case 'g': 11873255Simp g = (struct wallgroup *)malloc(sizeof *g); 11973255Simp g->next = grouplist; 12073255Simp g->name = optarg; 12173255Simp g->gid = -1; 12273255Simp grouplist = g; 12373255Simp break; 1241590Srgrimes case '?': 1251590Srgrimes default: 12628695Scharnier usage(); 1271590Srgrimes } 1281590Srgrimes argc -= optind; 1291590Srgrimes argv += optind; 1301590Srgrimes if (argc > 1) 13128695Scharnier usage(); 1321590Srgrimes 13373255Simp for (g = grouplist; g; g = g->next) { 13473255Simp grp = getgrnam(g->name); 13573320Simp if (grp != NULL) 13673255Simp g->gid = grp->gr_gid; 13773320Simp else 13873320Simp warnx("%s: no such group", g->name); 13973255Simp } 14073255Simp 1411590Srgrimes makemsg(*argv); 1421590Srgrimes 1431590Srgrimes iov.iov_base = mbuf; 1441590Srgrimes iov.iov_len = mbufsize; 1451590Srgrimes /* NOSTRICT */ 146200156Sed while ((utmp = getutxent()) != NULL) { 147200156Sed if (utmp->ut_type != USER_PROCESS) 1481590Srgrimes continue; 149200156Sed if (ttystat(utmp->ut_line) != 0) 150155875Scognet continue; 15173255Simp if (grouplist) { 15283082Sru ingroup = 0; 153200156Sed pw = getpwnam(utmp->ut_user); 15473255Simp if (!pw) 15573255Simp continue; 15673255Simp for (g = grouplist; g && ingroup == 0; g = g->next) { 15787675Smarkm if (g->gid == (gid_t)-1) 15873255Simp continue; 15973255Simp if (g->gid == pw->pw_gid) 16073255Simp ingroup = 1; 16183082Sru else if ((grp = getgrgid(g->gid)) != NULL) { 16283082Sru for (np = grp->gr_mem; *np; np++) { 163200156Sed if (strcmp(*np, utmp->ut_user) == 0) { 16483082Sru ingroup = 1; 16583082Sru break; 16683082Sru } 16783082Sru } 16883082Sru } 16973255Simp } 17073255Simp if (ingroup == 0) 17173255Simp continue; 17273255Simp } 173200156Sed if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5)) != NULL) 17428695Scharnier warnx("%s", p); 1751590Srgrimes } 1761590Srgrimes exit(0); 1771590Srgrimes} 1781590Srgrimes 17928695Scharnierstatic void 180201224Sedusage(void) 18128695Scharnier{ 18273320Simp (void)fprintf(stderr, "usage: wall [-g group] [file]\n"); 18328695Scharnier exit(1); 18428695Scharnier} 18528695Scharnier 1861590Srgrimesvoid 18773255Simpmakemsg(char *fname) 1881590Srgrimes{ 18973255Simp int cnt; 190233269Sglebius wchar_t ch; 1911590Srgrimes struct tm *lt; 1921590Srgrimes struct passwd *pw; 1931590Srgrimes struct stat sbuf; 19429434Sache time_t now; 1951590Srgrimes FILE *fp; 1961590Srgrimes int fd; 197233269Sglebius char hostname[MAXHOSTNAMELEN], tmpname[64]; 198233269Sglebius wchar_t *p, *tmp, lbuf[256], codebuf[13]; 19987675Smarkm const char *tty; 20069231Skris const char *whom; 20176367Skris gid_t egid; 2021590Srgrimes 20369231Skris (void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP); 20469231Skris if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+"))) 20569231Skris err(1, "can't open temporary file"); 2061590Srgrimes (void)unlink(tmpname); 2071590Srgrimes 2081590Srgrimes if (!nobanner) { 20969231Skris tty = ttyname(STDERR_FILENO); 21069231Skris if (tty == NULL) 21166557Sn_hibma tty = "no tty"; 21266557Sn_hibma 2131590Srgrimes if (!(whom = getlogin())) 2141590Srgrimes whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 2151590Srgrimes (void)gethostname(hostname, sizeof(hostname)); 2161590Srgrimes (void)time(&now); 2171590Srgrimes lt = localtime(&now); 2181590Srgrimes 2191590Srgrimes /* 2201590Srgrimes * all this stuff is to blank out a square for the message; 2211590Srgrimes * we wrap message lines at column 79, not 80, because some 2221590Srgrimes * terminals wrap after 79, some do not, and we can't tell. 2231590Srgrimes * Which means that we may leave a non-blank character 2241590Srgrimes * in column 80, but that can't be helped. 2251590Srgrimes */ 226233269Sglebius (void)fwprintf(fp, L"\r%79s\r\n", " "); 227233269Sglebius (void)swprintf(lbuf, sizeof(lbuf)/sizeof(wchar_t), 228233269Sglebius L"Broadcast Message from %s@%s", 2291590Srgrimes whom, hostname); 230233269Sglebius (void)fwprintf(fp, L"%-79.79S\007\007\r\n", lbuf); 231233269Sglebius (void)swprintf(lbuf, sizeof(lbuf)/sizeof(wchar_t), 232233269Sglebius L" (%s) at %d:%02d %s...", tty, 23363909Sasmodai lt->tm_hour, lt->tm_min, lt->tm_zone); 234233269Sglebius (void)fwprintf(fp, L"%-79.79S\r\n", lbuf); 2351590Srgrimes } 236233269Sglebius (void)fwprintf(fp, L"%79s\r\n", " "); 2371590Srgrimes 23876367Skris if (fname) { 23976367Skris egid = getegid(); 24076367Skris setegid(getgid()); 241233269Sglebius if (freopen(fname, "r", stdin) == NULL) 24276367Skris err(1, "can't read %s", fname); 243241848Seadler if (setegid(egid) != 0) 244241848Seadler err(1, "setegid failed"); 24576367Skris } 246223940Sobrien cnt = 0; 247233269Sglebius while (fgetws(lbuf, sizeof(lbuf)/sizeof(wchar_t), stdin)) { 248233269Sglebius for (p = lbuf; (ch = *p) != L'\0'; ++p, ++cnt) { 249233269Sglebius if (ch == L'\r') { 250233269Sglebius putwc(L'\r', fp); 25150776Sdbaker cnt = 0; 252175346Sdas continue; 253233269Sglebius } else if (ch == L'\n') { 2541590Srgrimes for (; cnt < 79; ++cnt) 255233269Sglebius putwc(L' ', fp); 256233269Sglebius putwc(L'\r', fp); 257233269Sglebius putwc(L'\n', fp); 258175346Sdas break; 259175346Sdas } 260175346Sdas if (cnt == 79) { 261233269Sglebius putwc(L'\r', fp); 262233269Sglebius putwc(L'\n', fp); 2631590Srgrimes cnt = 0; 264175346Sdas } 265233269Sglebius if (iswprint(ch) || iswspace(ch) || ch == L'\a' || ch == L'\b') { 266233269Sglebius putwc(ch, fp); 267233269Sglebius } else { 268233269Sglebius (void)swprintf(codebuf, sizeof(codebuf)/sizeof(wchar_t), L"<0x%X>", ch); 269233269Sglebius for (tmp = codebuf; *tmp != L'\0'; ++tmp) { 270233269Sglebius putwc(*tmp, fp); 27129434Sache if (++cnt == 79) { 272233269Sglebius putwc(L'\r', fp); 273233269Sglebius putwc(L'\n', fp); 27429434Sache cnt = 0; 27529434Sache } 27624729Sache } 277233269Sglebius --cnt; 27829434Sache } 2791590Srgrimes } 280175346Sdas } 281233269Sglebius (void)fwprintf(fp, L"%79s\r\n", " "); 2821590Srgrimes rewind(fp); 2831590Srgrimes 28428695Scharnier if (fstat(fd, &sbuf)) 28569231Skris err(1, "can't stat temporary file"); 2861590Srgrimes mbufsize = sbuf.st_size; 28728695Scharnier if (!(mbuf = malloc((u_int)mbufsize))) 28869231Skris err(1, "out of memory"); 28987675Smarkm if ((int)fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) 29069231Skris err(1, "can't read temporary file"); 2911590Srgrimes (void)close(fd); 2921590Srgrimes} 293