source: trunk/athena/bin/write/main.c @ 14322

Revision 14322, 10.7 KB checked in by ghudson, 25 years ago (diff)
Use ut_user (which is right everywhere except BSD) instead of ut_name. On BSD systems, add a #define to get ut_name.
Line 
1/* Copyright 1986-1999 by the Massachusetts Institute of Technology.
2 *
3 * Permission to use, copy, modify, and distribute this
4 * software and its documentation for any purpose and without
5 * fee is hereby granted, provided that the above copyright
6 * notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting
8 * documentation, and that the name of M.I.T. not be used in
9 * advertising or publicity pertaining to distribution of the
10 * software without specific, written prior permission.
11 * M.I.T. makes no representations about the suitability of
12 * this software for any purpose.  It is provided "as is"
13 * without express or implied warranty.
14 */
15
16/* This is the client side of the networked write system. */
17
18static const char rcsid[] = "$Id: main.c,v 1.3 2000-02-25 18:42:33 ghudson Exp $";
19
20#include <sys/types.h>
21#include <sys/socket.h>
22#include <sys/stat.h>
23
24#include <netinet/in.h>
25#include <arpa/inet.h>
26
27#include <ctype.h>
28#include <fcntl.h>
29#include <netdb.h>
30#include <pwd.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <time.h>
36#include <unistd.h>
37
38#ifdef HAVE_GETUTXENT
39#include <utmpx.h>
40#else /* HAVE_GETUTXENT */
41#include <utmp.h>
42#define ut_user ut_name
43#ifndef UTMP_FILE
44#ifdef _PATH_UTMP
45#define UTMP_FILE _PATH_UTMP
46#else
47#define UTMP_FILE "/var/adm/utmp"
48#endif
49#endif /* UTMP_FILE */
50struct utmp *getutxent(void);
51struct utmp *getutxline(const struct utmp *line);
52void setutxent(void);
53#endif /* HAVE_GETUTXENT */
54
55#ifndef HAVE_INET_ATON
56static int inet_aton(const char *str, struct in_addr *addr);
57#endif
58
59static int oktty(char *tty);
60static void eof(int);
61static int read_line(FILE *fp, char **buf, int *bufsize);
62
63int fromnet, tonet;
64FILE *f;
65
66int main(int argc, char **argv)
67{
68  int status, suser = getuid() == 0;
69  char *me, *myhost = NULL, *mytty = NULL;
70  char *him, *hishost = NULL, *histty = NULL, *histtyname = NULL;
71  char *p;
72  struct hostent *h;
73#ifdef HAVE_GETUTXENT
74  struct utmpx *ut, line;
75#else
76  struct utmp *ut, line;
77#endif
78  char *buf = NULL;
79  int fd, bufsize;
80  struct sigaction sa;
81
82  /* Determine the writing user. If invoked from writed, this will
83   * be passed as an argument. Otherwise, check utmp or passwd.
84   */
85  if (argc > 3 && suser && !strcmp(argv[1], "-f"))
86    {
87      struct sockaddr_in sin;
88      int sinlen = sizeof(sin);
89
90      fromnet = 1;
91      me = argv[2];
92
93      /* The host should be passed in as part of the argument, but
94       * we'll ignore what it says and figure it out for ourselves.
95       */
96      p = strchr(me, '@');
97      if (p)
98        *p = '\0';
99
100      if (getpeername(0, (struct sockaddr *)&sin, &sinlen) != 0)
101        {
102          perror("write: getpeername failed");
103          exit(1);
104        }
105      h = gethostbyaddr((char *)&sin.sin_addr, sinlen, AF_INET);
106      if (!h)
107        {
108          fprintf(stderr, "write: Can't determine your hostname.\n");
109          exit(1);
110        }
111      myhost = strdup(h->h_name);
112
113      argv += 2;
114      argc -= 2;
115    }
116  else
117    {
118      char hostbuf[256];
119
120      mytty = ttyname(0);
121      if (!mytty)
122        mytty = ttyname(1);
123      if (!mytty)
124        mytty = ttyname(2);
125      if (!mytty)
126        {
127          fprintf(stderr, "write: Can't find your tty.\n");
128          exit(1);
129        }
130
131      /* Check for writability. */
132      if (!oktty(mytty))
133        {
134          fprintf(stderr, "write: You have write permission turned off.\n");
135          if (!suser)
136            exit(1);
137        }
138
139      p = strchr(mytty + 1, '/');
140      if (p)
141        mytty = p + 1;
142
143      strncpy(line.ut_line, mytty, sizeof(line.ut_line));
144      ut = getutxline(&line);
145      if (ut)
146        {
147          me = malloc(sizeof(ut->ut_user) + 1);
148          strncpy(me, ut->ut_user, sizeof(ut->ut_user));
149          me[sizeof(ut->ut_user)] = '\0';
150        }
151     else
152        {
153          struct passwd *pw = getpwuid(getuid());
154          if (!pw)
155            {
156              fprintf(stderr, "write: Can't figure out who you are.\n");
157              exit(1);
158            }
159          me = strdup(pw->pw_name);
160        }
161
162      h = NULL;
163      if (!gethostname(hostbuf, sizeof(hostbuf)))
164        h = gethostbyname(hostbuf);
165      if (!h)
166        {
167          fprintf(stderr, "write: Can't determine your hostname.\n");
168          exit(1);
169        }
170      myhost = strdup(h->h_name);
171    }
172
173  /* Now see who we're writing to. */
174  if (argc < 2 || argc > 3)
175    {
176      fprintf(stderr, "Usage: write user [ttyname]\n");
177      exit(1);
178    }
179
180  him = argv[1];
181  hishost = strrchr(him, '@');
182  if (!fromnet && hishost)
183    {
184      *hishost++ = '\0';
185      h = gethostbyname(hishost);
186      if (!h)
187        {
188          struct in_addr addr;
189
190          if (inet_aton(hishost, &addr))
191            h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
192
193          if (!h)
194            {
195              fprintf(stderr, "write: unknown host: %s\n", hishost);
196              exit(1);
197            }
198        }
199      tonet = 1;
200    }
201
202  if (argc == 3)
203    {
204      /* User specified a tty. */
205      histtyname = argv[2];
206
207      /* If writing to a local user, make sure he's really logged in
208       * on that tty, and that he's accepting messages.
209       */
210      if (!tonet)
211        {
212          strncpy(line.ut_line, histtyname, sizeof(line.ut_line));
213          setutxent();
214          ut = getutxline(&line);
215          if (!ut || strncmp(ut->ut_user, him, sizeof(ut->ut_user)))
216            {
217              fprintf(stderr, "write: %s is not logged in on %s",
218                      him, histtyname);
219              exit(1);
220            }
221
222          histty = malloc(6 + strlen(histtyname));
223          if (!histty)
224            {
225              perror("write: malloc failed");
226              exit(1);
227            }
228          sprintf(histty, "/dev/%s", histtyname);
229          if (!oktty(histty))
230            {
231              fprintf(stderr, "write: Permission denied.\n");
232              exit(1);
233            }
234        }
235    }
236  else if (!tonet)
237    {
238      /* Find the tty(s) the user is logged in on. */
239      int found = 0, nomsg = 0;
240
241      histty = malloc(6 + sizeof(ut->ut_user));
242      setutxent();
243      while ((ut = getutxent()))
244        {
245          if (!strncmp(him, ut->ut_user, sizeof(ut->ut_user)))
246            {
247              if (!found || nomsg)
248                {
249                  sprintf(histty, "/dev/%.*s", (int)sizeof(ut->ut_line),
250                          ut->ut_line);
251                  nomsg = !oktty(histty);
252                }
253              found++;
254            }
255        }
256
257      if (!found)
258        {
259          fprintf(stderr, "write: %s not logged in.\n", him);
260          exit(1);
261        }
262      else if (nomsg)
263        {
264          fprintf(stderr, "write: Permission denied.\n");
265          exit(1);
266        }
267      else if (found > 1)
268        {
269          fprintf(stderr, "write: %s logged in more than once... "
270                  "writing to %s\n", him, histty + 5);
271        }
272    }
273
274
275  /* Open connection to recipient. */
276  if (tonet)
277    {
278      struct servent *s;
279      struct sockaddr_in sin;
280
281      s = getservbyname("write", "tcp");
282      if (!s)
283        {
284          fprintf(stderr, "write: unknown service write/tcp\n");
285          exit(1);
286        }
287
288      sin.sin_family = h->h_addrtype;
289      memmove(&sin.sin_addr, h->h_addr, h->h_length);
290      sin.sin_port = s->s_port;
291      fd = socket(h->h_addrtype, SOCK_STREAM, 0);
292      if (fd < 0)
293        {
294          perror("write: socket creation failed");
295          exit(1);
296        }
297      if (connect(fd, (struct sockaddr *)&sin, sizeof (sin)) < 0)
298        {
299          perror("write: Could not connect to remote host");
300          exit(1);
301        }
302
303      f = fdopen(fd, "r+");
304      if (histtyname)
305        fprintf(f, "%s@%s %s %s\r\n", me, myhost, him, histtyname);
306      else
307        fprintf(f, "%s@%s %s\r\n", me, myhost, him);
308      fflush(f);
309
310      /* Read response from server, which will be terminated with
311       * an empty line.
312       */
313      while ((status = read_line(f, &buf, &bufsize)) == 0 && *buf)
314        puts(buf);
315      if (status != 0)
316        exit(1);
317
318      /* Some OSes do not allow you to change from input to output on
319       * a FILE fdopen()ed "r+" without first explicitly setting the
320       * file position.
321       */
322      rewind(f);
323    }
324  else
325    {
326      time_t now = time(0);
327      struct tm *tm = localtime(&now);
328
329      f = fopen(histty, "w");
330      if (fromnet)
331        {
332          printf("\n");
333          fflush(stdout);
334          fprintf(f, "\r\nMessage from %s on %s at %d:%02d...\r\n\007\007\007",
335                  me, myhost, tm->tm_hour, tm->tm_min);
336        }
337      else
338        {
339          fprintf(f, "\r\nMessage from %s@%s at %d:%02d...\r\n\007\007\007",
340                  me, mytty, tm->tm_hour, tm->tm_min);
341        }
342      fflush(f);
343    }
344
345  sa.sa_flags = 0;
346  sigemptyset(&sa.sa_mask);
347  sa.sa_handler = eof;
348  sigaction(SIGINT, &sa, NULL);
349  sigaction(SIGHUP, &sa, NULL);
350  sigaction(SIGQUIT, &sa, NULL);
351
352  while (read_line(stdin, &buf, &bufsize) == 0)
353    {
354      char *p = buf;
355
356      while (*p)
357        {
358          if (!isascii((unsigned int)*p))
359            {
360              fputs("M-", f);
361              *p = toascii((unsigned int)*p);
362            }
363
364          if (isprint((unsigned int)*p) || isspace((unsigned int)*p))
365            putc(*p, f);
366          else
367            {
368              putc('^', f);
369              putc(*p ^ 0x40, f);
370            }
371
372          p++;
373        }
374      fputs("\r\n", f);
375      fflush(f);
376
377      if (ferror(f) || feof(f))
378        {
379          fprintf(stderr, "\n\007write: failed (%s logged out?)\n", him);
380          exit(1);
381        }
382    }
383  eof(0);
384  /* NOTREACHED */
385  exit(1);
386}
387
388static void eof(int sig)
389{
390  if (!tonet)
391    {
392      fputs("EOF\r\n", f);
393      fflush(f);
394    }
395  exit(0);
396}
397
398static int oktty(char *tty)
399{
400  struct stat st;
401
402  return access(tty, 0) == 0 && stat(tty, &st) == 0 && st.st_mode & S_IWGRP;
403}
404
405
406/* This is an internal function.  Its contract is to read a line from a
407 * file into a dynamically allocated buffer, zeroing the trailing newline
408 * if there is one.  The calling routine may call read_line multiple
409 * times with the same buf and bufsize pointers; *buf will be reallocated
410 * and *bufsize adjusted as appropriate.  The initial value of *buf
411 * should be NULL.  After the calling routine is done reading lines, it
412 * should free *buf.  This function returns 0 if a line was successfully
413 * read, 1 if the file ended, and -1 if there was an I/O error or if it
414 * ran out of memory.
415 */
416
417static int read_line(FILE *fp, char **buf, int *bufsize)
418{
419  char *newbuf;
420  int offset = 0, len;
421
422  if (*buf == NULL)
423    {
424      *buf = malloc(128);
425      if (!*buf)
426        return -1;
427      *bufsize = 128;
428    }
429
430  while (1)
431    {
432      if (!fgets(*buf + offset, *bufsize - offset, fp))
433        return (offset != 0) ? 0 : (ferror(fp)) ? -1 : 1;
434      len = offset + strlen(*buf + offset);
435      if ((*buf)[len - 1] == '\n')
436        {
437          (*buf)[len - 1] = 0;
438          return 0;
439        }
440      offset = len;
441
442      /* Allocate more space. */
443      newbuf = realloc(*buf, *bufsize * 2);
444      if (!newbuf)
445        return -1;
446      *buf = newbuf;
447      *bufsize *= 2;
448    }
449}
450
451
452#ifndef HAVE_GETUTXENT
453static int utfd;
454
455struct utmp *getutxent(void)
456{
457  static struct utmp ut;
458
459  if (!utfd)
460    setutxent();
461
462  if (read(utfd, &ut, sizeof(ut)) != sizeof(ut))
463    return NULL;
464  return &ut;
465}
466
467struct utmp *getutxline(const struct utmp *line)
468{
469  struct utmp *ut;
470
471  while ((ut = getutxent()))
472    {
473      if (!strncmp(line->ut_line, ut->ut_line, sizeof(line->ut_line)))
474        return ut;
475    }
476  return NULL;
477}
478
479void setutxent(void)
480{
481  if (utfd)
482    close(utfd);
483
484  utfd = open(UTMP_FILE, O_RDONLY);
485}
486#endif
487
488#ifndef HAVE_INET_ATON
489#ifndef INADDR_NONE
490#define INADDR_NONE 0xffffffff
491#endif
492
493static int inet_aton(const char *str, struct in_addr *addr)
494{
495  addr->s_addr = inet_addr(str);
496  return (addr->s_addr != INADDR_NONE || strcmp(str, "255.255.255.255") == 0);
497}
498#endif
Note: See TracBrowser for help on using the repository browser.