wall.c revision 1eabc4bec6d2a5ad71f6a1f0c019af5438d8b1bf
327N/A/*
327N/A * CDDL HEADER START
327N/A *
327N/A * The contents of this file are subject to the terms of the
327N/A * Common Development and Distribution License, Version 1.0 only
327N/A * (the "License"). You may not use this file except in compliance
327N/A * with the License.
327N/A *
327N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
327N/A * or http://www.opensolaris.org/os/licensing.
327N/A * See the License for the specific language governing permissions
327N/A * and limitations under the License.
327N/A *
327N/A * When distributing Covered Code, include this CDDL HEADER in each
327N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
327N/A * If applicable, add the following below this CDDL HEADER, with the
327N/A * fields enclosed by brackets "[]" replaced with your own identifying
327N/A * information: Portions Copyright [yyyy] [name of copyright owner]
327N/A *
327N/A * CDDL HEADER END
327N/A */
327N/A/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
5636N/A/* All Rights Reserved */
327N/A
5680N/A
327N/A
327N/A/*
5680N/A * Copyright 1988-2003 Sun Microsystems, Inc. All rights reserved.
864N/A * Use is subject to license terms.
327N/A */
3888N/A
618N/A/*
844N/A * Copyright 2012 Joyent, Inc. All rights reserved.
3888N/A *
327N/A * Copyright (c) 2013 Gary Mills
327N/A */
3888N/A
2899N/A#include <signal.h>
5680N/A#include <stdio.h>
5680N/A#include <stdlib.h>
327N/A#include <grp.h>
327N/A#include <sys/types.h>
327N/A#include <unistd.h>
327N/A#include <string.h>
327N/A#include <ctype.h>
327N/A#include <sys/stat.h>
327N/A#include <utmpx.h>
327N/A#include <sys/utsname.h>
327N/A#include <dirent.h>
327N/A#include <pwd.h>
327N/A#include <fcntl.h>
327N/A#include <time.h>
327N/A#include <errno.h>
5680N/A#include <locale.h>
327N/A#include <syslog.h>
5680N/A#include <sys/wait.h>
5680N/A#include <limits.h>
5680N/A#include <libzonecfg.h>
5680N/A#include <zone.h>
5680N/A#include <sys/contract/process.h>
5680N/A#include <libcontract.h>
5680N/A#include <sys/ctfs.h>
5680N/A
5680N/A/*
5680N/A * Use the full lengths from utmpx for user and line.
5680N/A */
5680N/A#define NMAX (sizeof (((struct utmpx *)0)->ut_user))
5680N/A#define LMAX (sizeof (((struct utmpx *)0)->ut_line))
5680N/A
5680N/Astatic char mesg[3000];
327N/Astatic char *infile;
327N/Astatic int gflag;
327N/Astatic struct group *pgrp;
4571N/Astatic char *grpname;
4571N/Astatic char line[MAXNAMLEN+1] = "???";
4571N/Astatic char systm[MAXNAMLEN+1];
4571N/Astatic time_t tloc;
4571N/Astatic struct utsname utsn;
4571N/Astatic char who[NMAX+1] = "???";
4571N/Astatic char time_buf[50];
1938N/A#define DATE_FMT "%a %b %e %H:%M:%S"
327N/A
3817N/Astatic void sendmes(struct utmpx *, zoneid_t);
3817N/Astatic void sendmes_tozone(zoneid_t, int);
3817N/Astatic int chkgrp(char *);
5636N/Astatic char *copy_str_till(char *, char *, char, int);
3817N/A
3817N/Astatic int init_template(void);
4878N/Aint contract_abandon_id(ctid_t);
3817N/A
3817N/Aint
3817N/Amain(int argc, char *argv[])
3817N/A{
3817N/A FILE *f;
4878N/A char *ptr, *start;
struct passwd *pwd;
char *term_name;
int c;
int aflag = 0;
int errflg = 0;
int zflg = 0;
int Zflg = 0;
char *zonename = NULL;
zoneid_t *zoneidlist = NULL;
uint_t nzids_saved, nzids = 0;
(void) setlocale(LC_ALL, "");
while ((c = getopt(argc, argv, "g:az:Z")) != EOF)
switch (c) {
case 'a':
aflag++;
break;
case 'g':
if (gflag) {
(void) fprintf(stderr,
"Only one group allowed\n");
return (1);
}
if ((pgrp = getgrnam(grpname = optarg)) == NULL) {
(void) fprintf(stderr, "Unknown group %s\n",
grpname);
return (1);
}
gflag++;
break;
case 'z':
zflg++;
zonename = optarg;
if (getzoneidbyname(zonename) == -1) {
(void) fprintf(stderr, "Specified zone %s "
"is invalid", zonename);
return (1);
}
break;
case 'Z':
Zflg++;
break;
case '?':
errflg++;
break;
}
if (errflg) {
(void) fprintf(stderr,
"Usage: wall [-a] [-g group] [-z zone] [-Z] [files...]\n");
return (1);
}
if (zflg && Zflg) {
(void) fprintf(stderr, "Cannot use -z with -Z\n");
return (1);
}
if (optind < argc)
infile = argv[optind];
if (uname(&utsn) == -1) {
(void) fprintf(stderr, "wall: uname() failed, %s\n",
strerror(errno));
return (2);
}
(void) strcpy(systm, utsn.nodename);
/*
* Get the name of the terminal wall is running from.
*/
if ((term_name = ttyname(fileno(stderr))) != NULL) {
/*
* skip the leading "/dev/" in term_name
*/
(void) strncpy(line, &term_name[5], sizeof (line) - 1);
}
if (who[0] == '?') {
if (pwd = getpwuid(getuid()))
(void) strncpy(&who[0], pwd->pw_name, sizeof (who));
}
f = stdin;
if (infile) {
f = fopen(infile, "r");
if (f == NULL) {
(void) fprintf(stderr, "Cannot open %s\n", infile);
return (1);
}
}
start = &mesg[0];
ptr = start;
while ((ptr - start) < 3000) {
size_t n;
if (fgets(ptr, &mesg[sizeof (mesg)] - ptr, f) == NULL)
break;
if ((n = strlen(ptr)) == 0)
break;
ptr += n;
}
(void) fclose(f);
/*
* If the request is from the rwall daemon then use the caller's
* name and host. We determine this if all of the following is true:
* 1) First 5 characters are "From "
* 2) Next non-white characters are of the form "name@host:"
*/
if (strcmp(line, "???") == 0) {
char rwho[MAXNAMLEN+1];
char rsystm[MAXNAMLEN+1];
char *cp;
if (strncmp(mesg, "From ", 5) == 0) {
cp = &mesg[5];
cp = copy_str_till(rwho, cp, '@', MAXNAMLEN + 1);
if (rwho[0] != '\0') {
cp = copy_str_till(rsystm, ++cp, ':',
MAXNAMLEN + 1);
if (rsystm[0] != '\0') {
(void) strcpy(systm, rsystm);
(void) strncpy(rwho, who,
sizeof (who));
(void) strcpy(line, "rpc.rwalld");
}
}
}
}
(void) time(&tloc);
(void) strftime(time_buf, sizeof (time_buf),
DATE_FMT, localtime(&tloc));
if (zflg != 0) {
if ((zoneidlist =
malloc(sizeof (zoneid_t))) == NULL ||
(*zoneidlist = getzoneidbyname(zonename)) == -1)
return (errno);
nzids = 1;
} else if (Zflg != 0) {
if (zone_list(NULL, &nzids) != 0)
return (errno);
again:
nzids *= 2;
if ((zoneidlist = malloc(nzids * sizeof (zoneid_t))) == NULL)
exit(errno);
nzids_saved = nzids;
if (zone_list(zoneidlist, &nzids) != 0) {
(void) free(zoneidlist);
return (errno);
}
if (nzids > nzids_saved) {
free(zoneidlist);
goto again;
}
}
if (zflg || Zflg) {
for (; nzids > 0; --nzids)
sendmes_tozone(zoneidlist[nzids-1], aflag);
free(zoneidlist);
} else
sendmes_tozone(getzoneid(), aflag);
return (0);
}
/*
* Copy src to destination upto but not including the delim.
* Leave dst empty if delim not found or whitespace encountered.
* Return pointer to next character (delim, whitespace, or '\0')
*/
static char *
copy_str_till(char *dst, char *src, char delim, int len)
{
int i = 0;
while (*src != '\0' && i < len) {
if (isspace(*src)) {
dst[0] = '\0';
return (src);
}
if (*src == delim) {
dst[i] = '\0';
return (src);
}
dst[i++] = *src++;
}
dst[0] = '\0';
return (src);
}
static void
sendmes_tozone(zoneid_t zid, int aflag) {
int i = 0;
char zonename[ZONENAME_MAX], root[MAXPATHLEN];
struct utmpx *p;
if (zid != getzoneid()) {
root[0] = '\0';
(void) getzonenamebyid(zid, zonename, ZONENAME_MAX);
(void) zone_get_rootpath(zonename, root, sizeof (root));
(void) strlcat(root, UTMPX_FILE, sizeof (root));
if (!utmpxname(root)) {
(void) fprintf(stderr, "Cannot open %s\n", root);
return;
}
} else {
(void) utmpxname(UTMPX_FILE);
}
setutxent();
while ((p = getutxent()) != NULL) {
if (p->ut_type != USER_PROCESS)
continue;
/*
* if (-a option OR NOT pty window login), send the message
*/
if (aflag || !nonuserx(*p))
sendmes(p, zid);
}
endutxent();
(void) alarm(60);
do {
i = (int)wait((int *)0);
} while (i != -1 || errno != ECHILD);
}
/*
* Note to future maintainers: with the change of wall to use the
* getutxent() API, the forked children (created by this function)
* must call _exit as opposed to exit. This is necessary to avoid
* unwanted fflushing of getutxent's stdio stream (caused by atexit
* processing).
*/
static void
sendmes(struct utmpx *p, zoneid_t zid)
{
int i;
char *s;
static char device[LMAX + 6];
char *bp;
int ibp;
FILE *f;
int fd, tmpl_fd;
boolean_t zoneenter = B_FALSE;
if (zid != getzoneid()) {
zoneenter = B_TRUE;
tmpl_fd = init_template();
if (tmpl_fd == -1) {
(void) fprintf(stderr, "Could not initialize "
"process contract");
return;
}
}
while ((i = (int)fork()) == -1) {
(void) alarm(60);
(void) wait((int *)0);
(void) alarm(0);
}
if (i)
return;
if (zoneenter && zone_enter(zid) == -1) {
char zonename[ZONENAME_MAX];
(void) getzonenamebyid(zid, zonename, ZONENAME_MAX);
(void) fprintf(stderr, "Could not enter zone "
"%s\n", zonename);
}
if (zoneenter)
(void) ct_tmpl_clear(tmpl_fd);
if (gflag)
if (!chkgrp(p->ut_user))
_exit(0);
(void) signal(SIGHUP, SIG_IGN);
(void) alarm(60);
s = &device[0];
(void) snprintf(s, sizeof (device), "/dev/%.*s", LMAX, p->ut_line);
/* check if the device is really a tty */
if ((fd = open(s, O_WRONLY|O_NOCTTY|O_NONBLOCK)) == -1) {
(void) fprintf(stderr, "Cannot send to %.*s on %s\n",
NMAX, p->ut_user, s);
perror("open");
(void) fflush(stderr);
_exit(1);
} else {
if (!isatty(fd)) {
(void) fprintf(stderr,
"Cannot send to device %.*s %s\n",
LMAX, p->ut_line,
"because it's not a tty");
openlog("wall", 0, LOG_AUTH);
syslog(LOG_CRIT, "%.*s in utmpx is not a tty\n",
LMAX, p->ut_line);
closelog();
(void) fflush(stderr);
_exit(1);
}
}
#ifdef DEBUG
(void) close(fd);
f = fopen("wall.debug", "a");
#else
f = fdopen(fd, "w");
#endif
if (f == NULL) {
(void) fprintf(stderr, "Cannot send to %-.*s on %s\n",
NMAX, &p->ut_user[0], s);
perror("open");
(void) fflush(stderr);
_exit(1);
}
(void) fprintf(f,
"\07\07\07Broadcast Message from %s (%s) on %s %19.19s",
who, line, systm, time_buf);
if (gflag)
(void) fprintf(f, " to group %s", grpname);
(void) fprintf(f, "...\n");
#ifdef DEBUG
(void) fprintf(f, "DEBUG: To %.*s on %s\n", NMAX, p->ut_user, s);
#endif
i = strlen(mesg);
for (bp = mesg; --i >= 0; bp++) {
ibp = (unsigned int)((unsigned char) *bp);
if (*bp == '\n')
(void) putc('\r', f);
if (isprint(ibp) || *bp == '\r' || *bp == '\013' ||
*bp == ' ' || *bp == '\t' || *bp == '\n' || *bp == '\007') {
(void) putc(*bp, f);
} else {
if (!isascii(*bp)) {
(void) fputs("M-", f);
*bp = toascii(*bp);
}
if (iscntrl(*bp)) {
(void) putc('^', f);
(void) putc(*bp + 0100, f);
}
else
(void) putc(*bp, f);
}
if (*bp == '\n')
(void) fflush(f);
if (ferror(f) || feof(f)) {
(void) printf("\n\007Write failed\n");
(void) fflush(stdout);
_exit(1);
}
}
(void) fclose(f);
(void) close(fd);
_exit(0);
}
static int
chkgrp(char *name)
{
int i;
char user[NMAX + 1];
(void) strlcpy(user, name, sizeof (user));
for (i = 0; pgrp->gr_mem[i] && pgrp->gr_mem[i][0]; i++) {
if (strcmp(user, pgrp->gr_mem[i]) == 0)
return (1);
}
return (0);
}
static int
init_template(void) {
int fd = 0;
int err = 0;
fd = open64(CTFS_ROOT "/process/template", O_RDWR);
if (fd == -1)
return (-1);
err |= ct_tmpl_set_critical(fd, 0);
err |= ct_tmpl_set_informative(fd, 0);
err |= ct_pr_tmpl_set_fatal(fd, CT_PR_EV_HWERR);
err |= ct_pr_tmpl_set_param(fd, CT_PR_PGRPONLY | CT_PR_REGENT);
if (err || ct_tmpl_activate(fd)) {
(void) close(fd);
return (-1);
}
return (fd);
}