Importing usr.bin/write

Replaces commands/write. No Minix-specific changes needed.

The NetBSD version lacks a few features that were present
in the Minix version: cbreak mode, verbose, and shell escapes,
but the main write(1) functionality is there and working.

Change-Id: I87b9589c54d3595d26247d221bb3d1f613feeb8c
This commit is contained in:
Thomas Cort 2013-10-25 15:25:55 -04:00
parent b36343fc42
commit 00709a7737
12 changed files with 590 additions and 321 deletions

View File

@ -29,7 +29,7 @@ SUBDIR= add_route arp ash at backup btrace \
telnetd term termcap tget time touch \
truncate tty udpstat umount uname \
unstack update uud uue version vol \
whereis which write writeisofs fetch \
whereis which writeisofs fetch \
zdump zmodem pkgin_cd pkgin_all \
worldstone updateboot update_bootcfg \
atnormalize dosread fdisk loadfont \

View File

@ -1,6 +0,0 @@
PROG= write
BINGRP= tty
BINMODE= 2755
MAN=
.include <bsd.prog.mk>

View File

@ -1,271 +0,0 @@
/* write - write to a logged in user Authors: N. Andrew and F. van Kempen */
/*
* Usage: write [-c] [-v] user [tty]
* -c Read & write one character at a time (cbreak mode)
* -v Verbose
*
* Version: 1.6 10/24/92
*
* NOTES: Write requires 1.4a (or higher) libraries,
* for getopt(), strchr().
*
* Authors: Nick Andrew (nick@nswitgould.oz) - Public Domain
* Fred van Kempen (minixug!waltje@kyber.uucp)
*/
#include <sys/types.h>
#include <fcntl.h>
#include <pwd.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utmp.h>
#include <time.h>
#include <stdio.h>
#include <paths.h>
#if 0
static char *Version = "@(#) WRITE 1.6 (10/24/92)";
#endif
int otty; /* file desc of callee's terminal */
short int cbreak = 0; /* are we in CBREAK (-c) mode? */
short int verbose = 0; /* are we in VERBOSE (-v) mode? */
short int writing = 0; /* is there a connection? */
char *user = NULL; /* callee's user name */
char *tty = NULL; /* callee's terminal if given */
char *ourtty = NULL; /* our terminal name */
struct termios ttyold, ttynew; /* our tty controlling structs */
extern int optind;
int main(int argc, char **argv);
char *finduser(void);
void settty(char *utty);
void sayhello(void);
void escape(char *cmd);
void writetty(void);
void usage(void);
void intr(int dummy);
char *finduser()
{
/* Search the UTMP database for the user we want. */
static char utmptty[16];
struct utmp utmp;
struct passwd *userptr;
int utmpfd;
ourtty = ttyname(0);
if (ourtty == NULL) ourtty = "/dev/console";
if (user == NULL) exit(-1);
if ((userptr = getpwnam(user)) == NULL) {
fprintf(stderr, "No such user: %s\n", user);
return(NULL);
}
if (verbose) fprintf(stderr, "Trying to write to %s\n",
userptr->pw_gecos);
if ((utmpfd = open(_PATH_UTMP, O_RDONLY)) < 0) {
fprintf(stderr, "Cannot open utmp file\n");
return(NULL);
}
utmptty[0] = '\0';
/* We want to find if 'user' is logged on, and return in utmptty[]
* 'user' `s terminal, and if 'user' is logged onto the tty the
* caller specified, return that tty name. */
while (read(utmpfd, (char *) &utmp, sizeof(utmp)) == sizeof(utmp)) {
/* is this the user we are looking for? */
if (strncmp(utmp.ut_name, user, sizeof(utmp.ut_name))) continue;
strcpy(utmptty, utmp.ut_line);
/* is he on the terminal we want to write to? */
if (tty == NULL || !strcmp(utmptty, tty)) {
break;
}
}
if (utmptty[0] == '\0') {
fprintf(stderr, "%s is not logged on\n", user);
return( NULL);
}
if (tty != NULL && strcmp(utmptty, tty)) {
fprintf(stderr, "%s is logged onto %s, not %s\n", user, utmptty, tty);
return( NULL);
}
close(utmpfd);
if (verbose) fprintf(stderr, "Writing to %s on %s\n", user, utmptty);
return(utmptty);
}
void settty(utty)
char *utty; /* name of terminal found in utmp */
{
/* Open other person's terminal and setup our own terminal. */
char buff[48];
sprintf(buff, "/dev/%s", utty);
if ((otty = open(buff, O_WRONLY)) < 0) {
fprintf(stderr, "Cannot open %s to write to %s\n", utty, user);
fprintf(stderr, "It may have write permission turned off\n");
exit(-1);
}
tcgetattr(0, &ttyold);
tcgetattr(0, &ttynew);
ttynew.c_lflag &= ~(ICANON|ECHO);
signal(SIGINT, intr);
if (cbreak) tcsetattr(0, TCSANOW, &ttynew);
}
void sayhello()
{
struct passwd *pw;
char buff[128];
time_t now;
char *sp;
time(&now);
pw = getpwuid(getuid());
if (pw == NULL) {
fprintf(stderr, "unknown user\n");
exit(-1);
}
if ((sp = strrchr(ourtty, '/')) != NULL)
++sp;
else
sp = ourtty;
sprintf(buff, "\nMessage from %s (%s) %-24.24s...\n",
pw->pw_name, sp, ctime(&now));
write(otty, buff, strlen(buff));
printf("\007\007");
fflush(stdout);
}
void escape(cmd)
char *cmd;
{
/* Shell escape. */
register char *x;
write(1, "!\n", 2);
for (x = cmd; *x; ++x)
if (*x == '\n') *x = '\0';
system(cmd);
write(1, "!\n", 2);
}
void writetty()
{
/* The write loop. */
char line[80];
int n, cb_esc;
writing = 1;
cb_esc = 0;
while ((n = read(0, line, 79)) > 0) {
if (line[0] == '\004') break; /* EOT */
if (cbreak && line[0] == '\n') cb_esc = 1;
if (cbreak) write(1, line, n);
if (line[0] == '!') {
if (cbreak && cb_esc) {
cb_esc = 0;
tcsetattr(0, TCSANOW, &ttyold);
read(0, line, 79);
escape(line);
tcsetattr(0, TCSANOW, &ttynew);
} else if (cbreak)
write(otty, line, n);
else
escape(&line[1]);
continue;
}
write(otty, line, n);
}
write(1, "\nEOT\n", 5);
write(otty, "\nEOT\n", 5);
}
void usage()
{
fprintf(stderr, "usage: write [-c] [-v] user [tty]\n");
fprintf(stderr, "\t-c : cbreak mode\n\t-v : verbose\n");
exit(-1);
}
int main(argc, argv)
int argc;
char *argv[];
{
register int c;
char *sp;
setbuf(stdout, (char *) NULL);
/* Parse options. */
while ((c = getopt(argc, argv, "cv")) != EOF) switch (c) {
case 'c': cbreak = 1; break;
case 'v': verbose = 1; break;
default:
usage();
}
/* Parse user and tty arguments */
if (optind < argc) {
user = argv[optind++];
/* WTMP usernames are 1-8 chars */
if (strlen(user) > 8) *(user + 8) = '\0';
if (optind < argc) {
tty = argv[optind++];
if (optind < argc) usage();
}
} else
usage();
sp = finduser(); /* find which tty to write onto */
if (sp != NULL) { /* did we find one? */
settty(sp); /* setup our terminal */
sayhello(); /* print the initial message */
writetty(); /* the write loop */
tcsetattr(0, TCSANOW, &ttyold);
exit(0);
}
return(-1);
}
void intr(dummy)
int dummy; /* to satisfy the prototype */
{
/* The interrupt key has been hit. exit cleanly. */
signal(SIGINT, SIG_IGN);
fprintf(stderr, "\nInterrupt. Exiting write\n");
tcsetattr(0, TCSANOW, &ttyold);
if (writing) write(otty, "\nEOT\n", 5);
exit(0);
}

View File

@ -21,7 +21,7 @@ MAN= ash.1 at.1 \
term.1 termcap.1 tget.1 time.1 true.1 \
truncate.1 tty.1 umount.1 uname.1 \
uud.1 uue.1 vol.1 whereis.1 which.1 \
write.1 yap.1 linkfarm.1 pkg_view.1
yap.1 linkfarm.1 pkg_view.1
MLINKS += ash.1 sh.1
MLINKS += ash.1 ..1

View File

@ -1,41 +0,0 @@
.TH WRITE 1
.SH NAME
write \- send a message to a logged-in user
.SH SYNOPSIS
\fBwrite\fR [\fB\-cv\fR] \fIuser\fR [\fItty\fR]\fR
.br
.de FL
.TP
\\fB\\$1\\fR
\\$2
..
.de EX
.TP 20
\\fB\\$1\\fR
# \\$2
..
.SH OPTIONS
.TP 5
.B \-c
# Use cbreak mode
.TP 5
.B \-v
# Verbose mode
.SH EXAMPLES
.TP 20
.B write ast
# Send a message to ast
.TP 20
.B write ast tty00
# Send a message to ast on tty00
.SH DESCRIPTION
.PP
\fIWrite\fR lets a user send messages to another logged-in user.
Lines typed by the user appear on the other user's screen a line at a time
(a character at a time in the case of cbreak mode).
The file \fI/usr/adm/wtmp\fR is searched to determine which tty to send to.
If the user is logged onto more than one terminal, the \fItty\fR argument
selects the terminal. Type CTRL- D to terminate the command.
Use ! as a shell escape.
.SH "SEE ALSO"
.BR mail (1).

View File

@ -214,6 +214,7 @@
2012/10/17 12:00:00,usr.bin/wc
2013/03/22 12:00:00,usr.bin/whatis
2013/03/15 12:00:00,usr.bin/who
2012/10/17 12:00:00,usr.bin/write
2012/10/17 12:00:00,usr.bin/xinstall
2013/03/15 12:00:00,usr.bin/yes
2012/02/10 16:16:12,usr.sbin/chroot

View File

@ -32,7 +32,7 @@ SUBDIR= \
uuidgen \
\
wc whatis who \
xargs xinstall yes
write xargs xinstall yes
.if !defined(__MINIX)
SUBDIR+= ../external/zlib/pigz/bin/pigz

15
usr.bin/write/Makefile Normal file
View File

@ -0,0 +1,15 @@
# $NetBSD: Makefile,v 1.8 2007/05/28 12:06:33 tls Exp $
# from: @(#)Makefile 8.1 (Berkeley) 6/6/93
.include <bsd.own.mk>
USE_FORT?= yes # setgid
PROG= write
SRCS= write.c utmpentry.c term_chk.c
BINMODE=2555
BINGRP= tty
.PATH.c: ${NETBSDSRCDIR}/usr.bin/who
CPPFLAGS+=-I${NETBSDSRCDIR}/usr.bin/who -DSUPPORT_UTMPX -DSUPPORT_UTMP
.include <bsd.prog.mk>

137
usr.bin/write/term_chk.c Normal file
View File

@ -0,0 +1,137 @@
/* $NetBSD: term_chk.c,v 1.8 2009/04/14 07:59:17 lukem Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: term_chk.c,v 1.8 2009/04/14 07:59:17 lukem Exp $");
#endif
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <paths.h>
#include <fcntl.h>
#include <string.h>
#include <err.h>
#include "term_chk.h"
/*
* term_chk - check that a terminal exists, and get the message bit
* and the access time
*/
int
term_chk(uid_t uid, const char *tty, int *msgsokP, time_t *atimeP, int ismytty,
gid_t saved_egid)
{
char path[MAXPATHLEN];
struct stat s;
int i, fd, serrno;
if (strstr(tty, "../") != NULL) {
errno = EINVAL;
return -1;
}
i = snprintf(path, sizeof path, _PATH_DEV "%s", tty);
if (i < 0 || i >= (int)sizeof(path)) {
errno = ENOMEM;
return -1;
}
(void)setegid(saved_egid);
fd = open(path, O_WRONLY, 0);
serrno = errno;
(void)setegid(getgid());
errno = serrno;
if (fd == -1)
return(-1);
if (fstat(fd, &s) == -1)
goto error;
if (!isatty(fd))
goto error;
if (s.st_uid != uid && uid != 0) {
errno = EPERM;
goto error;
}
if (msgsokP)
*msgsokP = (s.st_mode & S_IWGRP) != 0; /* group write bit */
if (atimeP)
*atimeP = s.st_atime;
if (ismytty)
(void)close(fd);
return ismytty ? 0 : fd;
error:
if (fd != -1) {
serrno = errno;
(void)close(fd);
errno = serrno;
}
return -1;
}
char *
check_sender(time_t *atime, uid_t myuid, gid_t saved_egid)
{
int myttyfd;
int msgsok;
char *mytty;
/* check that sender has write enabled */
if (isatty(fileno(stdin)))
myttyfd = fileno(stdin);
else if (isatty(fileno(stdout)))
myttyfd = fileno(stdout);
else if (isatty(fileno(stderr)))
myttyfd = fileno(stderr);
else if (atime == NULL)
return NULL;
else
errx(1, "Cannot find your tty");
if ((mytty = ttyname(myttyfd)) == NULL)
err(1, "Cannot find the name of your tty");
if (strncmp(mytty, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0)
mytty += sizeof(_PATH_DEV) - 1;
if (term_chk(myuid, mytty, &msgsok, atime, 1, saved_egid) == -1)
err(1, "%s%s", _PATH_DEV, mytty);
if (!msgsok) {
warnx(
"You have write permission turned off; no reply possible");
}
return mytty;
}

36
usr.bin/write/term_chk.h Normal file
View File

@ -0,0 +1,36 @@
/* $NetBSD: term_chk.h,v 1.2 2003/08/07 11:17:48 agc Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
int term_chk(uid_t, const char *, int *, time_t *, int, gid_t);
char *check_sender(time_t *, uid_t, gid_t);

106
usr.bin/write/write.1 Normal file
View File

@ -0,0 +1,106 @@
.\" $NetBSD: write.1,v 1.6 2003/08/07 11:17:48 agc Exp $
.\"
.\" Copyright (c) 1989, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" from: @(#)write.1 8.1 (Berkeley) 6/6/93
.\"
.Dd June 6, 1993
.Dt WRITE 1
.Os
.Sh NAME
.Nm write
.Nd send a message to another user
.Sh SYNOPSIS
.Nm
.Ar user
.Op Ar ttyname
.Sh DESCRIPTION
.Nm
allows you to communicate with other users, by copying lines from
your terminal to theirs.
.Pp
When you run the
.Nm
command, the user you are writing to gets a message of the form:
.Pp
.Dl Message from yourname@yourhost on yourtty at hh:mm ...
.Pp
Any further lines you enter will be copied to the specified user's
terminal.
If the other user wants to reply, they must run
.Nm
as well.
.Pp
When you are done, type an end-of-file or interrupt character.
The other user will see the message
.Ql EOF
indicating that the
conversation is over.
.Pp
You can prevent people (other than the super-user) from writing to you
with the
.Xr mesg 1
command.
Some commands, for example
.Xr nroff 1
and
.Xr pr 1 ,
disallow writing automatically, so that your output isn't overwritten.
.Pp
If the user you want to write to is logged in on more than one terminal,
you can specify which terminal to write to by specifying the terminal
name as the second operand to the
.Nm
command.
Alternatively, you can let
.Nm
select one of the terminals \- it will pick the one with the shortest
idle time.
This is so that if the user is logged in at work and also dialed up from
home, the message will go to the right place.
.Pp
The traditional protocol for writing to someone is that the string
.Ql \-o ,
either at the end of a line or on a line by itself, means that it's the
other person's turn to talk.
The string
.Ql oo
means that the person believes the conversation to be
over.
.Sh SEE ALSO
.Xr mesg 1 ,
.Xr talk 1 ,
.Xr who 1
.Sh HISTORY
A
.Nm
command appeared in
.At v6 .

292
usr.bin/write/write.c Normal file
View File

@ -0,0 +1,292 @@
/* $NetBSD: write.c,v 1.27 2011/09/06 18:46:35 joerg Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1989, 1993\
The Regents of the University of California. All rights reserved.");
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)write.c 8.2 (Berkeley) 4/27/95";
#else
__RCSID("$NetBSD: write.c,v 1.27 2011/09/06 18:46:35 joerg Exp $");
#endif
#endif /* not lint */
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <time.h>
#include <fcntl.h>
#include <paths.h>
#include <pwd.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include "utmpentry.h"
#include "term_chk.h"
__dead static void done(int);
static void do_write(int, const char *, const uid_t);
static void wr_fputs(char *);
static int search_utmp(char *, char *, uid_t, gid_t);
static int utmp_chk(const char *, const char *);
int
main(int argc, char **argv)
{
time_t atime;
uid_t myuid, uid;
int msgsok, ttyfd;
char *mytty;
gid_t saved_egid = getegid();
if (setegid(getgid()) == -1)
err(1, "setegid");
myuid = getuid();
ttyfd = -1;
mytty = check_sender(&atime, myuid, saved_egid);
/* check args */
switch (argc) {
case 2:
ttyfd = search_utmp(argv[1], mytty, myuid, saved_egid);
break;
case 3:
if (!strncmp(argv[2], _PATH_DEV, strlen(_PATH_DEV)))
argv[2] += strlen(_PATH_DEV);
if (uid_from_user(argv[1], &uid) == -1)
errx(1, "%s: unknown user", argv[1]);
if (utmp_chk(argv[1], argv[2]))
errx(1, "%s is not logged in on %s",
argv[1], argv[2]);
ttyfd = term_chk(uid, argv[2], &msgsok, &atime, 0, saved_egid);
if (ttyfd == -1)
err(1, "%s%s", _PATH_DEV, argv[2]);
if (myuid && !msgsok)
errx(1, "%s has messages disabled on %s",
argv[1], argv[2]);
break;
default:
(void)fprintf(stderr, "usage: write user [tty]\n");
exit(1);
}
if (setgid(getgid()) == -1)
err(1, "setgid");
do_write(ttyfd, mytty, myuid);
done(0);
/* NOTREACHED */
#ifdef __GNUC__
return (0);
#endif
}
/*
* utmp_chk - checks that the given user is actually logged in on
* the given tty
*/
static int
utmp_chk(const char *user, const char *tty)
{
struct utmpentry *ep;
(void)getutentries(NULL, &ep);
for (; ep; ep = ep->next)
if (strcmp(user, ep->name) == 0 && strcmp(tty, ep->line) == 0)
return(0);
return(1);
}
/*
* search_utmp - search utmp for the "best" terminal to write to
*
* Ignores terminals with messages disabled, and of the rest, returns
* the one with the most recent access time. Returns as value the number
* of the user's terminals with messages enabled, or -1 if the user is
* not logged in at all.
*
* Special case for writing to yourself - ignore the terminal you're
* writing from, unless that's the only terminal with messages enabled.
*/
static int
search_utmp(char *user, char *mytty, uid_t myuid, gid_t saved_egid)
{
char tty[MAXPATHLEN];
time_t bestatime, atime;
int nloggedttys, nttys, msgsok, user_is_me;
struct utmpentry *ep;
int fd, nfd;
uid_t uid;
if (uid_from_user(user, &uid) == -1)
errx(1, "%s: unknown user", user);
(void)getutentries(NULL, &ep);
nloggedttys = nttys = 0;
bestatime = 0;
user_is_me = 0;
fd = -1;
for (; ep; ep = ep->next)
if (strcmp(user, ep->name) == 0) {
++nloggedttys;
nfd = term_chk(uid, ep->line, &msgsok, &atime, 0,
saved_egid);
if (nfd == -1)
continue; /* bad term? skip */
if (myuid && !msgsok) {
close(nfd);
continue; /* skip ttys with msgs off */
}
if (strcmp(ep->line, mytty) == 0) {
user_is_me = 1;
if (fd == -1)
fd = nfd;
else
close(nfd);
continue; /* don't write to yourself */
}
++nttys;
if (atime > bestatime) {
bestatime = atime;
(void)strlcpy(tty, ep->line, sizeof(tty));
close(fd);
fd = nfd;
} else
close(nfd);
}
if (nloggedttys == 0)
errx(1, "%s is not logged in", user);
if (nttys == 0) {
if (user_is_me) /* ok, so write to yourself! */
return fd;
errx(1, "%s has messages disabled", user);
} else if (nttys > 1)
warnx("%s is logged in more than once; writing to %s",
user, tty);
return fd;
}
/*
* do_write - actually make the connection
*/
static void
do_write(int ttyfd, const char *mytty, const uid_t myuid)
{
const char *login;
char *nows;
struct passwd *pwd;
time_t now;
char host[MAXHOSTNAMELEN + 1], line[512];
/* Determine our login name before we re-open stdout */
if ((login = getlogin()) == NULL) {
if ((pwd = getpwuid(myuid)) != NULL)
login = pwd->pw_name;
else login = "???";
}
if (dup2(ttyfd, STDOUT_FILENO) == -1)
err(1, "dup2");
(void)signal(SIGINT, done);
(void)signal(SIGHUP, done);
(void)close(ttyfd);
/* print greeting */
if (gethostname(host, sizeof(host)) < 0)
(void)strlcpy(host, "???", sizeof(host));
else
host[sizeof(host) - 1] = '\0';
now = time(NULL);
nows = ctime(&now);
nows[16] = '\0';
(void)printf("\r\n\a\a\aMessage from %s@%s on %s at %s ...\r\n",
login, host, mytty, nows + 11);
while (fgets(line, sizeof(line), stdin) != NULL)
wr_fputs(line);
}
/*
* done - cleanup and exit
*/
static void
done(int signo)
{
(void)write(STDOUT_FILENO, "EOF\r\n", sizeof("EOF\r\n") - 1);
if (signo == 0)
exit(0);
else
_exit(0);
}
/*
* wr_fputs - like fputs(), but makes control characters visible and
* turns \n into \r\n
*/
static void
wr_fputs(char *s)
{
unsigned char c;
#define PUTC(c) if (putchar(c) == EOF) goto err;
for (; *s != '\0'; ++s) {
c = toascii(*s);
if (c == '\n') {
PUTC('\r');
} else if (!isprint(c) && !isspace(c) && c != '\a') {
PUTC('^');
c ^= 0x40; /* DEL to ?, others to alpha */
}
PUTC(c);
}
return;
err: err(1, NULL);
#undef PUTC
}