]> Zhao Yanbai Git Server - minix.git/commitdiff
Importing usr.bin/write 69/1069/1
authorThomas Cort <tcort@minix3.org>
Fri, 25 Oct 2013 19:25:55 +0000 (15:25 -0400)
committerThomas Cort <tcort@minix3.org>
Fri, 25 Oct 2013 21:21:52 +0000 (17:21 -0400)
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

12 files changed:
commands/Makefile
commands/write/Makefile [deleted file]
commands/write/write.c [deleted file]
man/man1/Makefile
man/man1/write.1 [deleted file]
releasetools/nbsd_ports
usr.bin/Makefile
usr.bin/write/Makefile [new file with mode: 0644]
usr.bin/write/term_chk.c [new file with mode: 0644]
usr.bin/write/term_chk.h [new file with mode: 0644]
usr.bin/write/write.1 [new file with mode: 0644]
usr.bin/write/write.c [new file with mode: 0644]

index 45df0a654ea28580111751a6b0c0505afb657879..35f06d9be315aaff2bf978d72353ce8958663bd8 100644 (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 \
diff --git a/commands/write/Makefile b/commands/write/Makefile
deleted file mode 100644 (file)
index e1e0e19..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-PROG=  write
-BINGRP= tty
-BINMODE= 2755
-MAN=
-
-.include <bsd.prog.mk>
diff --git a/commands/write/write.c b/commands/write/write.c
deleted file mode 100644 (file)
index 335f49d..0000000
+++ /dev/null
@@ -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);
-}
index da66405a1e2562f6ba49e6e6b463433043eda68a..dc31a2c1147da154b84b40ff0acc467f05048928 100644 (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
diff --git a/man/man1/write.1 b/man/man1/write.1
deleted file mode 100644 (file)
index 6dd955d..0000000
+++ /dev/null
@@ -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).
index d0e7a5df1e6e0ef3fe42d71c548849c950a50f29..31428cf93ba317c83e3b5e654fb2bbd7376dcf90 100644 (file)
 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
index 7655e184133cc48ae3bcf58a0d7387f968c176f9..28b24327ccde1f1449eb7b33d54a5683360e2169 100644 (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
diff --git a/usr.bin/write/Makefile b/usr.bin/write/Makefile
new file mode 100644 (file)
index 0000000..8439439
--- /dev/null
@@ -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>
diff --git a/usr.bin/write/term_chk.c b/usr.bin/write/term_chk.c
new file mode 100644 (file)
index 0000000..d7d2cbe
--- /dev/null
@@ -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;
+}
diff --git a/usr.bin/write/term_chk.h b/usr.bin/write/term_chk.h
new file mode 100644 (file)
index 0000000..aca4568
--- /dev/null
@@ -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);
diff --git a/usr.bin/write/write.1 b/usr.bin/write/write.1
new file mode 100644 (file)
index 0000000..28d614c
--- /dev/null
@@ -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 .
diff --git a/usr.bin/write/write.c b/usr.bin/write/write.c
new file mode 100644 (file)
index 0000000..945e92f
--- /dev/null
@@ -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
+}