1  /*
       2   * Copyright (c) 1989, 1993
       3   *	The Regents of the University of California.  All rights reserved.
       4   *
       5   * Redistribution and use in source and binary forms, with or without
       6   * modification, are permitted provided that the following conditions
       7   * are met:
       8   * 1. Redistributions of source code must retain the above copyright
       9   *    notice, this list of conditions and the following disclaimer.
      10   * 2. Redistributions in binary form must reproduce the above copyright
      11   *    notice, this list of conditions and the following disclaimer in the
      12   *    documentation and/or other materials provided with the distribution.
      13   * 3. All advertising materials mentioning features or use of this software
      14   *    must display the following acknowledgement:
      15   *	This product includes software developed by the University of
      16   *	California, Berkeley and its contributors.
      17   * 4. Neither the name of the University nor the names of its contributors
      18   *    may be used to endorse or promote products derived from this software
      19   *    without specific prior written permission.
      20   *
      21   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
      22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      24   * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
      25   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      27   * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      28   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      31   * SUCH DAMAGE.
      32   *
      33   * Modified Sun Mar 12 10:39:22 1995, faith@cs.unc.edu for Linux
      34   *
      35   */
      36  
      37   /* 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL>
      38    * - added Native Language Support
      39    * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
      40    * - fixed strerr(errno) in gettext calls
      41    */
      42  
      43  #include <sys/types.h>
      44  #include <sys/param.h>
      45  #include <sys/uio.h>
      46  #include <signal.h>
      47  #include <fcntl.h>
      48  #include <dirent.h>
      49  #include <errno.h>
      50  #include <paths.h>
      51  #include <unistd.h>
      52  #include <stdio.h>
      53  #include <string.h>
      54  #include <stdlib.h>
      55  
      56  #include "nls.h"
      57  #include "closestream.h"
      58  #include "pathnames.h"
      59  #include "ttymsg.h"
      60  
      61  #define ERR_BUFLEN	(MAXNAMLEN + 1024)
      62  
      63  /*
      64   * Display the contents of a uio structure on a terminal.  Used by wall(1),
      65   * syslogd(8), and talkd(8).  Forks and finishes in child if write would block,
      66   * waiting up to tmout seconds.  Returns pointer to error string on unexpected
      67   * error; string is not newline-terminated.  Various "normal" errors are
      68   * ignored (exclusive-use, lack of permission, etc.).
      69   */
      70  char *
      71  ttymsg(struct iovec *iov, size_t iovcnt, char *line, int tmout) {
      72  	static char device[MAXNAMLEN];
      73  	static char errbuf[ERR_BUFLEN];
      74  	size_t cnt, left;
      75  	ssize_t wret;
      76  	struct iovec localiov[6];
      77  	int fd, forked = 0;
      78  	ssize_t	len = 0;
      79  
      80  	if (iovcnt > ARRAY_SIZE(localiov)) {
      81  		snprintf(errbuf, sizeof(errbuf), _("internal error: too many iov's"));
      82  		return errbuf;
      83  	}
      84  
      85  	/* The old code here rejected the line argument when it contained a '/',
      86  	   saying: "A slash may be an attempt to break security...".
      87  	   However, if a user can control the line argument here
      88  	   then they can make this routine write to /dev/hda or /dev/sda
      89  	   already. So, this test was worthless, and these days it is
      90  	   also wrong since people use /dev/pts/xxx. */
      91  
      92  	len = snprintf(device, sizeof(device), "%s%s", _PATH_DEV, line);
      93  	if (len < 0 || (size_t)len >= sizeof(device)) {
      94  		snprintf(errbuf, sizeof(errbuf), _("excessively long line arg"));
      95  		return errbuf;
      96  	}
      97  
      98  	/*
      99  	 * open will fail on slip lines or exclusive-use lines
     100  	 * if not running as root; not an error.
     101  	 */
     102  	if ((fd = open(device, O_WRONLY|O_NONBLOCK, 0)) < 0) {
     103  		if (errno == EBUSY || errno == EACCES)
     104  			return NULL;
     105  
     106  		len = snprintf(errbuf, sizeof(errbuf), "%s: %m", device);
     107  		if (len < 0 || (size_t)len >= sizeof(errbuf))
     108  			snprintf(errbuf, sizeof(errbuf), _("open failed"));
     109  		return errbuf;
     110  	}
     111  
     112  	for (cnt = left = 0; cnt < iovcnt; ++cnt)
     113  		left += iov[cnt].iov_len;
     114  
     115  	for (;;) {
     116  		wret = writev(fd, iov, iovcnt);
     117  		if (wret >= (ssize_t) left)
     118  			break;
     119  		if (wret >= 0) {
     120  			left -= wret;
     121  			if (iov != localiov) {
     122  				memmove(localiov, iov,
     123  				    iovcnt * sizeof(struct iovec));
     124  				iov = localiov;
     125  			}
     126  			for (cnt = 0; wret >= (ssize_t) iov->iov_len; ++cnt) {
     127  				wret -= iov->iov_len;
     128  				++iov;
     129  				--iovcnt;
     130  			}
     131  			if (wret) {
     132  				iov->iov_base = (char *) iov->iov_base + wret;
     133  				iov->iov_len -= wret;
     134  			}
     135  			continue;
     136  		}
     137  		if (errno == EWOULDBLOCK) {
     138  			int cpid, flags;
     139  			sigset_t sigmask;
     140  
     141  			if (forked) {
     142  				close(fd);
     143  				_exit(EXIT_FAILURE);
     144  			}
     145  			cpid = fork();
     146  			if (cpid < 0) {
     147  				len = snprintf(errbuf, sizeof(errbuf), _("fork: %m"));
     148  				if (len < 0 || (size_t)len >= sizeof(errbuf))
     149  					snprintf(errbuf, sizeof(errbuf), _("cannot fork"));
     150  				close(fd);
     151  				return errbuf;
     152  			}
     153  			if (cpid) {	/* parent */
     154  				close(fd);
     155  				return NULL;
     156  			}
     157  			forked++;
     158  			/* wait at most tmout seconds */
     159  			signal(SIGALRM, SIG_DFL);
     160  			signal(SIGTERM, SIG_DFL); /* XXX */
     161  			sigemptyset(&sigmask);
     162  			sigprocmask (SIG_SETMASK, &sigmask, NULL);
     163  			alarm((u_int)tmout);
     164  			flags = fcntl(fd, F_GETFL);
     165  			fcntl(flags, F_SETFL, (long) (flags & ~O_NONBLOCK));
     166  			continue;
     167  		}
     168  		/*
     169  		 * We get ENODEV on a slip line if we're running as root,
     170  		 * and EIO if the line just went away.
     171  		 */
     172  		if (errno == ENODEV || errno == EIO)
     173  			break;
     174  		if (close_fd(fd) != 0)
     175  			warn(_("write failed: %s"), device);
     176  		if (forked)
     177  			_exit(EXIT_FAILURE);
     178  
     179  		len = snprintf(errbuf, sizeof(errbuf), "%s: %m", device);
     180  		if (len < 0 || (size_t)len >= sizeof(errbuf))
     181  			snprintf(errbuf, sizeof(errbuf),
     182  					_("%s: BAD ERROR, message is "
     183  					  "far too long"), device);
     184  		return errbuf;
     185  	}
     186  
     187  	close(fd);
     188  
     189  	if (forked)
     190  		_exit(EXIT_SUCCESS);
     191  	return NULL;
     192  }