wall.c revision 242166
1219019Sgabor/* 2219019Sgabor * Copyright (c) 1988, 1990, 1993 3219019Sgabor * The Regents of the University of California. All rights reserved. 4219019Sgabor * 5219019Sgabor * Redistribution and use in source and binary forms, with or without 6219019Sgabor * modification, are permitted provided that the following conditions 7219019Sgabor * are met: 8219019Sgabor * 1. Redistributions of source code must retain the above copyright 9219019Sgabor * notice, this list of conditions and the following disclaimer. 10219019Sgabor * 2. Redistributions in binary form must reproduce the above copyright 11219019Sgabor * notice, this list of conditions and the following disclaimer in the 12219019Sgabor * documentation and/or other materials provided with the distribution. 13219019Sgabor * 4. Neither the name of the University nor the names of its contributors 14219019Sgabor * may be used to endorse or promote products derived from this software 15219019Sgabor * without specific prior written permission. 16219019Sgabor * 17219019Sgabor * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18219019Sgabor * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19219019Sgabor * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20219019Sgabor * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21219019Sgabor * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22219019Sgabor * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23219019Sgabor * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24219019Sgabor * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25219019Sgabor * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26219019Sgabor * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27219019Sgabor * SUCH DAMAGE. 28219019Sgabor */ 29219019Sgabor 30219019Sgabor#include <sys/cdefs.h> 31219019Sgabor 32219019Sgabor__FBSDID("$FreeBSD: stable/9/usr.bin/wall/wall.c 242166 2012-10-27 01:20:48Z eadler $"); 33219019Sgabor 34219019Sgabor#ifndef lint 35219019Sgaborstatic const char copyright[] = 36219019Sgabor"@(#) Copyright (c) 1988, 1990, 1993\n\ 37219019Sgabor The Regents of the University of California. All rights reserved.\n"; 38219019Sgabor#endif 39219019Sgabor 40219019Sgabor#ifndef lint 41219019Sgaborstatic const char sccsid[] = "@(#)wall.c 8.2 (Berkeley) 11/16/93"; 42219019Sgabor#endif 43219019Sgabor 44219019Sgabor/* 45219019Sgabor * This program is not related to David Wall, whose Stanford Ph.D. thesis 46219019Sgabor * is entitled "Mechanisms for Broadcast and Selective Broadcast". 47219019Sgabor */ 48219019Sgabor 49219019Sgabor#include <sys/param.h> 50219019Sgabor#include <sys/stat.h> 51219019Sgabor#include <sys/uio.h> 52219019Sgabor 53219019Sgabor#include <ctype.h> 54219019Sgabor#include <err.h> 55219019Sgabor#include <grp.h> 56219019Sgabor#include <locale.h> 57219019Sgabor#include <paths.h> 58219019Sgabor#include <pwd.h> 59219019Sgabor#include <stdio.h> 60219019Sgabor#include <stdlib.h> 61219019Sgabor#include <string.h> 62219019Sgabor#include <time.h> 63219019Sgabor#include <unistd.h> 64219019Sgabor#include <utmpx.h> 65219019Sgabor#include <wchar.h> 66219019Sgabor#include <wctype.h> 67219019Sgabor 68219019Sgabor#include "ttymsg.h" 69219019Sgabor 70219019Sgaborstatic void makemsg(char *); 71219019Sgaborstatic void usage(void); 72219019Sgabor 73219019Sgaborstruct wallgroup { 74219019Sgabor struct wallgroup *next; 75219019Sgabor char *name; 76219019Sgabor gid_t gid; 77219019Sgabor} *grouplist; 78219019Sgaborint nobanner; 79219019Sgaborint mbufsize; 80219019Sgaborchar *mbuf; 81219019Sgabor 82219019Sgaborstatic int 83219019Sgaborttystat(char *line) 84219019Sgabor{ 85219019Sgabor struct stat sb; 86219019Sgabor char ttybuf[MAXPATHLEN]; 87219019Sgabor 88219019Sgabor (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); 89219019Sgabor if (stat(ttybuf, &sb) == 0) { 90219019Sgabor return (0); 91219019Sgabor } else 92219019Sgabor return (-1); 93219019Sgabor} 94219019Sgabor 95219019Sgaborint 96219019Sgabormain(int argc, char *argv[]) 97219019Sgabor{ 98219019Sgabor struct iovec iov; 99219019Sgabor struct utmpx *utmp; 100219019Sgabor int ch; 101219019Sgabor int ingroup; 102219019Sgabor struct wallgroup *g; 103219019Sgabor struct group *grp; 104219019Sgabor char **np; 105219019Sgabor const char *p; 106219019Sgabor struct passwd *pw; 107219019Sgabor 108219019Sgabor (void)setlocale(LC_CTYPE, ""); 109219019Sgabor 110219019Sgabor while ((ch = getopt(argc, argv, "g:n")) != -1) 111219019Sgabor switch (ch) { 112219019Sgabor case 'n': 113219019Sgabor /* undoc option for shutdown: suppress banner */ 114219019Sgabor if (geteuid() == 0) 115219019Sgabor nobanner = 1; 116219019Sgabor break; 117250984Sed case 'g': 118219019Sgabor g = (struct wallgroup *)malloc(sizeof *g); 119219019Sgabor g->next = grouplist; 120219019Sgabor g->name = optarg; 121219019Sgabor g->gid = -1; 122219019Sgabor grouplist = g; 123219019Sgabor break; 124219019Sgabor case '?': 125219019Sgabor default: 126219019Sgabor usage(); 127219019Sgabor } 128219019Sgabor argc -= optind; 129219019Sgabor argv += optind; 130219019Sgabor if (argc > 1) 131219019Sgabor usage(); 132219019Sgabor 133219019Sgabor for (g = grouplist; g; g = g->next) { 134219019Sgabor grp = getgrnam(g->name); 135219019Sgabor if (grp != NULL) 136219019Sgabor g->gid = grp->gr_gid; 137219019Sgabor else 138219019Sgabor warnx("%s: no such group", g->name); 139219019Sgabor } 140219019Sgabor 141219019Sgabor makemsg(*argv); 142219019Sgabor 143219019Sgabor iov.iov_base = mbuf; 144219019Sgabor iov.iov_len = mbufsize; 145219019Sgabor /* NOSTRICT */ 146219019Sgabor while ((utmp = getutxent()) != NULL) { 147219019Sgabor if (utmp->ut_type != USER_PROCESS) 148219019Sgabor continue; 149219019Sgabor if (ttystat(utmp->ut_line) != 0) 150219019Sgabor continue; 151219019Sgabor if (grouplist) { 152219019Sgabor ingroup = 0; 153219019Sgabor pw = getpwnam(utmp->ut_user); 154219019Sgabor if (!pw) 155219019Sgabor continue; 156219019Sgabor for (g = grouplist; g && ingroup == 0; g = g->next) { 157219019Sgabor if (g->gid == (gid_t)-1) 158219019Sgabor continue; 159219019Sgabor if (g->gid == pw->pw_gid) 160219019Sgabor ingroup = 1; 161219019Sgabor else if ((grp = getgrgid(g->gid)) != NULL) { 162219019Sgabor for (np = grp->gr_mem; *np; np++) { 163219019Sgabor if (strcmp(*np, utmp->ut_user) == 0) { 164219019Sgabor ingroup = 1; 165219019Sgabor break; 166219019Sgabor } 167219019Sgabor } 168219019Sgabor } 169219019Sgabor } 170219019Sgabor if (ingroup == 0) 171219019Sgabor continue; 172219019Sgabor } 173219019Sgabor if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5)) != NULL) 174219019Sgabor warnx("%s", p); 175219019Sgabor } 176219019Sgabor exit(0); 177219019Sgabor} 178219019Sgabor 179219019Sgaborstatic void 180219019Sgaborusage(void) 181219019Sgabor{ 182219019Sgabor (void)fprintf(stderr, "usage: wall [-g group] [file]\n"); 183219019Sgabor exit(1); 184219019Sgabor} 185219019Sgabor 186219019Sgaborvoid 187219019Sgabormakemsg(char *fname) 188219019Sgabor{ 189219019Sgabor int cnt; 190219019Sgabor wchar_t ch; 191219019Sgabor struct tm *lt; 192219019Sgabor struct passwd *pw; 193219019Sgabor struct stat sbuf; 194219019Sgabor time_t now; 195219019Sgabor FILE *fp; 196219019Sgabor int fd; 197219019Sgabor char hostname[MAXHOSTNAMELEN], tmpname[64]; 198219019Sgabor wchar_t *p, *tmp, lbuf[256], codebuf[13]; 199219019Sgabor const char *tty; 200219019Sgabor const char *whom; 201219019Sgabor gid_t egid; 202219019Sgabor 203219019Sgabor (void)snprintf(tmpname, sizeof(tmpname), "%s/wall.XXXXXX", _PATH_TMP); 204219019Sgabor if ((fd = mkstemp(tmpname)) == -1 || !(fp = fdopen(fd, "r+"))) 205219019Sgabor err(1, "can't open temporary file"); 206219019Sgabor (void)unlink(tmpname); 207219019Sgabor 208219019Sgabor if (!nobanner) { 209219019Sgabor tty = ttyname(STDERR_FILENO); 210219019Sgabor if (tty == NULL) 211219019Sgabor tty = "no tty"; 212219019Sgabor 213219019Sgabor if (!(whom = getlogin())) 214219019Sgabor whom = (pw = getpwuid(getuid())) ? pw->pw_name : "???"; 215219019Sgabor (void)gethostname(hostname, sizeof(hostname)); 216219019Sgabor (void)time(&now); 217219019Sgabor lt = localtime(&now); 218219019Sgabor 219219019Sgabor /* 220219019Sgabor * all this stuff is to blank out a square for the message; 221219019Sgabor * we wrap message lines at column 79, not 80, because some 222219019Sgabor * terminals wrap after 79, some do not, and we can't tell. 223219019Sgabor * Which means that we may leave a non-blank character 224219019Sgabor * in column 80, but that can't be helped. 225219019Sgabor */ 226219019Sgabor (void)fwprintf(fp, L"\r%79s\r\n", " "); 227219019Sgabor (void)swprintf(lbuf, sizeof(lbuf)/sizeof(wchar_t), 228219019Sgabor L"Broadcast Message from %s@%s", 229219019Sgabor whom, hostname); 230219019Sgabor (void)fwprintf(fp, L"%-79.79S\007\007\r\n", lbuf); 231219019Sgabor (void)swprintf(lbuf, sizeof(lbuf)/sizeof(wchar_t), 232219019Sgabor L" (%s) at %d:%02d %s...", tty, 233219019Sgabor lt->tm_hour, lt->tm_min, lt->tm_zone); 234219019Sgabor (void)fwprintf(fp, L"%-79.79S\r\n", lbuf); 235219019Sgabor } 236219019Sgabor (void)fwprintf(fp, L"%79s\r\n", " "); 237219019Sgabor 238219019Sgabor if (fname) { 239219019Sgabor egid = getegid(); 240219019Sgabor setegid(getgid()); 241219019Sgabor if (freopen(fname, "r", stdin) == NULL) 242219019Sgabor err(1, "can't read %s", fname); 243219019Sgabor if (setegid(egid) != 0) 244219019Sgabor err(1, "setegid failed"); 245219019Sgabor } 246219019Sgabor cnt = 0; 247219019Sgabor while (fgetws(lbuf, sizeof(lbuf)/sizeof(wchar_t), stdin)) { 248219019Sgabor for (p = lbuf; (ch = *p) != L'\0'; ++p, ++cnt) { 249219019Sgabor if (ch == L'\r') { 250219019Sgabor putwc(L'\r', fp); 251219019Sgabor cnt = 0; 252219019Sgabor continue; 253219019Sgabor } else if (ch == L'\n') { 254219019Sgabor for (; cnt < 79; ++cnt) 255219019Sgabor putwc(L' ', fp); 256219019Sgabor putwc(L'\r', fp); 257219019Sgabor putwc(L'\n', fp); 258219019Sgabor break; 259219019Sgabor } 260219019Sgabor if (cnt == 79) { 261219019Sgabor putwc(L'\r', fp); 262219019Sgabor putwc(L'\n', fp); 263219019Sgabor cnt = 0; 264219019Sgabor } 265219019Sgabor if (iswprint(ch) || iswspace(ch) || ch == L'\a' || ch == L'\b') { 266219019Sgabor putwc(ch, fp); 267219019Sgabor } else { 268219019Sgabor (void)swprintf(codebuf, sizeof(codebuf)/sizeof(wchar_t), L"<0x%X>", ch); 269219019Sgabor for (tmp = codebuf; *tmp != L'\0'; ++tmp) { 270219019Sgabor putwc(*tmp, fp); 271219019Sgabor if (++cnt == 79) { 272219019Sgabor putwc(L'\r', fp); 273219019Sgabor putwc(L'\n', fp); 274219019Sgabor cnt = 0; 275219019Sgabor } 276219019Sgabor } 277219019Sgabor --cnt; 278219019Sgabor } 279219019Sgabor } 280219019Sgabor } 281219019Sgabor (void)fwprintf(fp, L"%79s\r\n", " "); 282219019Sgabor rewind(fp); 283219019Sgabor 284219019Sgabor if (fstat(fd, &sbuf)) 285219019Sgabor err(1, "can't stat temporary file"); 286219019Sgabor mbufsize = sbuf.st_size; 287219019Sgabor if (!(mbuf = malloc((u_int)mbufsize))) 288219019Sgabor err(1, "out of memory"); 289219019Sgabor if ((int)fread(mbuf, sizeof(*mbuf), mbufsize, fp) != mbufsize) 290219019Sgabor err(1, "can't read temporary file"); 291219019Sgabor (void)close(fd); 292219019Sgabor} 293219019Sgabor