Files
Arquivotheca.Solaris-2.5/cmd/write/write.c
seta75D 7c4988eac0 Init
2021-10-11 19:38:01 -03:00

579 lines
14 KiB
C
Executable File

/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/* Portions Copyright (c) 1988, Sun Microsystems, Inc. */
/* All Rights Reserved. */
#ident "@(#)write.c 1.16 94/10/03 SMI" /* SVr4.0 1.19 */
/* Program to communicate with other users of the system. */
/* Usage: write user_name [terminal] */
#include <string.h>
#include <stdio.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/utsname.h>
#include <euc.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <utmp.h>
#include <pwd.h>
#include <fcntl.h>
#include <stdarg.h>
#include <locale.h>
#include <stdlib.h>
#include <limits.h>
#include <wctype.h>
#include <errno.h>
#include <syslog.h>
#define TRUE 1
#define FALSE 0
#define FAILURE -1
#define DATE_FMT "%a %b %e %H:%M:%S"
#define UTMP_HACK /* Enable work around until utmp is world writable */
/*
* DATE-TIME format
* %a abbreviated weekday name
* %b abbreviated month name
* %e day of month
* %H hour - 24 hour clock
* %M minute
* %S second
*
*/
static int permit1(int);
static int permit(char *);
static int readeuc(int, char *, int);
static void setsignals();
static void shellcmd(char *);
static void openfail();
static void eof();
static struct utsname utsn;
static FILE *fp; /* File pointer for receipient's terminal */
static char *rterm, *receipient; /* Pointer to receipient's terminal & name */
static char *thissys;
void
main(argc, argv)
int argc;
char **argv;
{
register int i;
register struct utmp *ubuf;
static struct utmp self;
char ownname[sizeof (self.ut_user) + 1];
static char rterminal[] = "/dev/\0 2345678901";
extern char *rterm, *receipient;
char *terminal, *ownterminal, *oterminal;
short count;
extern FILE *fp;
char input[134+MB_LEN_MAX];
register char *ptr;
long tod;
char time_buf[40];
struct passwd *passptr;
char badterm[20][20];
register int bad = 0;
uid_t myuid;
register char *bp;
unsigned int ibp;
int n;
wchar_t wc;
register c;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
while ((c = getopt(argc, argv, "")) != EOF)
switch (c) {
case '?':
(void) fprintf(stderr, "Usage: write %s\n",
gettext("user_name [terminal]"));
exit(2);
}
myuid = geteuid();
uname(&utsn);
thissys = utsn.nodename;
/* Set "rterm" to location where receipient's terminal will go. */
rterm = &rterminal[sizeof ("/dev/") - 1];
terminal = NULL;
if (--argc <= 0)
{
(void) fprintf(stderr, "Usage: write %s\n",
gettext("user_name [terminal]"));
exit(1);
}
else
{
receipient = *++argv;
}
/* Was a terminal name supplied? If so, save it. */
if (--argc > 1)
{
(void) fprintf(stderr, "Usage: write %s\n",
gettext("user_name [terminal]"));
exit(1);
}
else
terminal = *++argv;
/* One of the standard file descriptors must be attached to a */
/* terminal in "/dev". */
if ((ownterminal = ttyname(fileno(stdin))) == NULL &&
(ownterminal = ttyname(fileno(stdout))) == NULL &&
(ownterminal = ttyname(fileno(stderr))) == NULL)
{
(void) fprintf(stderr,
gettext("I cannot determine your terminal name."
" No reply possible.\n"));
ownterminal = "/dev/???";
}
/* Set "ownterminal" past the "/dev/" at the beginning of */
/* the device name. */
oterminal = ownterminal + sizeof ("/dev/")-1;
/* Scan through the "utmp" file for your own entry and the */
/* entry for the person we want to send to. */
for (self.ut_pid = 0, count = 0; (ubuf = getutent()) != NULL; )
{
/* Is this a USER_PROCESS entry? */
if (ubuf->ut_type == USER_PROCESS)
{
/* Is it our entry? (ie. The line matches ours?) */
if (strncmp(&ubuf->ut_line[0], oterminal,
sizeof (ubuf->ut_line)) == 0) self = *ubuf;
/* Is this the person we want to send to? */
if (strncmp(receipient, &ubuf->ut_user[0],
sizeof (ubuf->ut_user)) == 0)
{
/* If a terminal name was supplied, is this login at the correct */
/* terminal? If not, ignore. If it is right place, copy over the */
/* name. */
if (terminal != NULL)
{
if (strncmp(terminal, &ubuf->ut_line[0],
sizeof (ubuf->ut_line)) == 0)
{
strncpy(rterm, &ubuf->ut_line[0],
sizeof (ubuf->ut_line)+1);
if (myuid && !permit(rterminal)) {
bad++;
rterm[0] = '\0';
}
}
}
/* If no terminal was supplied, then take this terminal if no */
/* other terminal has been encountered already. */
else
{
/* If this is the first encounter, copy the string into */
/* "rterminal". */
if (*rterm == '\0')
{
strncpy(rterm,
&ubuf->ut_line[0], sizeof (ubuf->ut_line)+1);
if (myuid && !permit(rterminal)) {
strcpy(badterm[bad++], rterm);
rterm[0] = '\0';
} else if (bad > 0) {
(void) fprintf(stderr,
gettext(
"%s is logged on more than one place.\n"
"You are connected to \"%s\".\nOther locations are:\n"),
receipient, rterm);
for (i = 0; i < bad; i++)
(void) fprintf(stderr, "%s\n", badterm[i]);
}
}
/* If this is the second terminal, print out the first. In all */
/* cases of multiple terminals, list out all the other terminals */
/* so the user can restart knowing what her/his choices are. */
else if (terminal == NULL)
{
if (count == 1 && bad == 0)
{
(void) fprintf(stderr,
gettext(
"%s is logged on more than one place.\n"
"You are connected to \"%s\".\nOther locations are:\n"),
receipient, rterm);
}
fwrite(&ubuf->ut_line[0], sizeof (ubuf->ut_line),
1, stderr);
(void) fprintf(stderr, "\n");
}
count++;
} /* End of "else" */
} /* End of "else if (strncmp" */
} /* End of "if (USER_PROCESS" */
} /* End of "for(count=0" */
/* Did we find a place to talk to? If we were looking for a */
/* specific spot and didn't find it, complain and quit. */
if (terminal != NULL && *rterm == '\0')
{
if (bad > 0) {
(void) fprintf(stderr, gettext("Permission denied.\n"));
exit(1);
} else {
#ifdef UTMP_HACK
strcat(rterminal, terminal);
strcpy(rterm, terminal);
if (self.ut_pid == 0) {
if ((passptr = getpwuid(getuid())) == (struct passwd *)NULL) {
(void) fprintf(stderr,
gettext("Cannot determine who you are.\n"));
exit(1);
}
strncpy(&ownname[0], &passptr->pw_name[0],
sizeof (ownname));
}
if (!permit(rterminal)) {
(void) fprintf(stderr,
gettext("%s permission denied\n"), terminal);
exit(1);
}
#else
(void) fprintf(stderr, gettext("%s is not at \"%s\".\n"),
receipient, terminal);
exit(1);
#endif /* UTMP_HACK */
}
}
/* If we were just looking for anyplace to talk and didn't find */
/* one, complain and quit. */
/* If permissions prevent us from sending to this person - exit */
else if (*rterm == '\0')
{
if (bad > 0)
(void) fprintf(stderr, gettext("Permission denied.\n"));
else
(void) fprintf(stderr,
gettext("%s is not logged on.\n"), receipient);
exit(1);
}
/* Did we find our own entry? */
else if (self.ut_pid == 0)
{
/* Use the user id instead of utmp name if the entry in the */
/* utmp file couldn't be found. */
if ((passptr = getpwuid(getuid())) == (struct passwd *)NULL)
{
(void) fprintf(stderr,
gettext("Cannot determine who you are.\n"));
exit(1);
}
strncpy(&ownname[0], &passptr->pw_name[0], sizeof (ownname));
}
else
{
strncpy(&ownname[0], self.ut_user, sizeof (self.ut_user));
}
ownname[sizeof (ownname)-1] = '\0';
if (!permit1(1))
(void) fprintf(stderr,
gettext("Warning: You have your terminal set to \"mesg -n\"."
" No reply possible.\n"));
/* Try to open up the line to the receipient's terminal. */
signal(SIGALRM, openfail);
alarm(5);
fp = fopen(&rterminal[0], "w");
alarm(0);
/* Catch signals SIGHUP, SIGINT, SIGQUIT, and SIGTERM, and send */
/* <EOT> message to receipient before dying away. */
setsignals(eof);
/* Get the time of day, convert it to a string and throw away the */
/* year information at the end of the string. */
time(&tod);
cftime(time_buf, dcgettext(NULL, DATE_FMT, LC_TIME), &tod);
(void) fprintf(fp,
gettext("\n\007\007\007\tMessage from %s on %s (%s) [ %s ] ...\n"),
&ownname[0], thissys, oterminal, time_buf);
fflush(fp);
(void) fprintf(stderr, "\007\007");
/* Get input from user and send to receipient unless it begins */
/* with a !, when it is to be a shell command. */
while ((i = readeuc(0, &input[0], sizeof (input))) > 0) {
ptr = &input[0];
/* Is this a shell command? */
if (*ptr == '!')
shellcmd(++ptr);
/* Send line to the receipient. */
else {
if (myuid && !permit1(fileno(fp))) {
(void) fprintf(stderr,
gettext("Can no longer write to %s\n"), rterminal);
break;
}
/*
* All non-printable characters are displayed using a special notation:
* Control characters shall be displayed using the two character
* sequence of ^ (carat) and the ASCII character - decimal 64 greater
* that the character being encoded - eg., a \003 is displayed ^C.
* Characters with the eighth bit set shall be displayed using
* the three or four character meta notation - e.g., \372 is
* displayed M-z and \203 is displayed M-^C.
*/
for (bp = &input[0]; --i >= 0; bp++) {
ibp = (unsigned int) *bp;
if (*bp == '\n')
putc('\r', fp);
if (*bp == ' ' ||
*bp == '\t' || *bp == '\n' ||
*bp == '\r' || *bp == '\013' ||
*bp == '\007') {
putc(*bp, fp);
} else if (((n = mbtowc(&wc, bp, MB_LEN_MAX)) > 0) &&
iswprint(wc)) {
for (; n > 0; --n, --i, ++bp)
putc(*bp, fp);
bp--, ++i;
} else {
if (!isascii(*bp)) {
fputs("M-", fp);
*bp = toascii(*bp);
}
if (iscntrl(*bp)) {
putc('^', fp);
/* add decimal 64 to the control character */
putc(*bp + 0100, fp);
}
else
putc(*bp, fp);
}
if (*bp == '\n')
fflush(fp);
if (ferror(fp) || feof(fp)) {
printf(gettext(
"\n\007Write failed (%s logged out?)\n"),
receipient);
exit(1);
}
} /* for */
fflush(fp);
} /* else */
} /* while */
/* Since "end of file" received, send <EOT> message to receipient. */
eof();
}
static void
setsignals(catch)
void (*catch)();
{
signal(SIGHUP, catch);
signal(SIGINT, catch);
signal(SIGQUIT, catch);
signal(SIGTERM, catch);
}
static void
shellcmd(command)
char *command;
{
register pid_t child;
extern void eof();
if ((child = fork()) == (pid_t)FAILURE)
{
(void) fprintf(stderr,
gettext("Unable to fork. Try again later.\n"));
return;
} else if (child == (pid_t)0) {
/* Reset the signals to the default actions and exec a shell. */
if (setgid(getgid()) < 0)
exit(1);
execl("/usr/bin/sh", "sh", "-c", command, 0);
exit(0);
}
else
{
/* Allow user to type <del> and <quit> without dying during */
/* commands. */
signal(SIGINT, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
/* As parent wait around for user to finish spunoff command. */
while (wait(NULL) != child);
/* Reset the signals to their normal state. */
setsignals(eof);
}
(void) fprintf(stdout, "!\n");
}
static void
openfail()
{
extern char *rterm, *receipient;
(void) fprintf(stderr,
gettext("Timeout trying to open %s's line(%s).\n"),
receipient, rterm);
exit(1);
}
static void
eof()
{
extern FILE *fp;
(void) fprintf(fp, "%s\n", gettext("<EOT>"));
exit(0);
}
/*
* permit: check mode of terminal - if not writable by all disallow writing to
* (even the user him/herself cannot therefore write to their own tty)
*/
static int
permit(term)
char *term;
{
struct stat buf;
int fildes;
if ((fildes = open(term, O_WRONLY|O_NOCTTY)) < 0)
return (0);
/* check if the device really is a tty */
if (!isatty(fildes)) {
(void) fprintf(stderr,
gettext("%s in utmp is not a tty\n"), term);
openlog("write", 0, LOG_AUTH);
syslog(LOG_CRIT, "%s in utmp is not a tty\n", term);
closelog();
close(fildes);
return (0);
}
fstat(fildes, &buf);
close(fildes);
return (buf.st_mode & (S_IWGRP|S_IWOTH));
}
/*
* permit1: check mode of terminal - if not writable by all disallow writing
* to (even the user him/herself cannot therefore write to their own tty)
*/
/* this is used with fstat (which is faster than stat) where possible */
static int
permit1(fildes)
int fildes;
{
struct stat buf;
fstat(fildes, &buf);
return (buf.st_mode & (S_IWGRP|S_IWOTH));
}
/*
Read a string of EUC characters from specified file.
* The requested # of bytes are attempted to read.
* readeuc() tries to complete the last multibyte character
* by calling read() again, when the first read() call
* left the last char imcomplete. The caller must reserve
* nbytereq+MB_LEN_MAX bytes for the buffer. When the attempt
* is failed, it truncate the last char.
* Returns the number of bytes that constitutes the valid EUC characters.
*/
static int readeuc(d, buf, nbytereq)
int d;
char *buf;
int nbytereq;
{
char *cp, *nextp, *lastp;
int n;
int delta;
n = read(d, buf, nbytereq);
if (n <= 0)
return (n);
/* Scan the result to test the completeness of each EUC characters. */
nextp = buf;
lastp = buf + n; /* Lastp points to the first junk byte. */
while (nextp < lastp) {
cp = nextp;
nextp += euclen((const unsigned char *)cp);
}
/* How many bytes needed to complete the last char? */
delta = nextp - lastp;
if (delta > 0) {
n = read(d, lastp, delta); /* Try reading that many bytes. */
if (n != delta) {
/* Failed! Pretend we dindn't read the garbage bytes. */
nextp = cp;
}
}
return (nextp-buf); /* Return # of bytes. */
}