11169Sbill /* 239571Sbostic * Copyright (c) 1989 The Regents of the University of California. 339571Sbostic * All rights reserved. 439571Sbostic * 539571Sbostic * This code is derived from software contributed to Berkeley by 639571Sbostic * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. 739571Sbostic * 839571Sbostic * Redistribution and use in source and binary forms are permitted 939571Sbostic * provided that the above copyright notice and this paragraph are 1039571Sbostic * duplicated in all such forms and that any documentation, 1139571Sbostic * advertising materials, and other materials related to such 1239571Sbostic * distribution and use acknowledge that the software was developed 1339571Sbostic * by the University of California, Berkeley. The name of the 1439571Sbostic * University may not be used to endorse or promote products derived 1539571Sbostic * from this software without specific prior written permission. 1639571Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1739571Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1839571Sbostic * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 191169Sbill */ 201169Sbill 2139571Sbostic #ifndef lint 2239571Sbostic char copyright[] = 2339571Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 2439571Sbostic All rights reserved.\n"; 2539571Sbostic #endif /* not lint */ 2639571Sbostic 2739571Sbostic #ifndef lint 28*39620Sleres static char sccsid[] = "@(#)write.c 4.20 (Berkeley) 11/25/89"; 2939571Sbostic #endif /* not lint */ 3039571Sbostic 3139571Sbostic #include <sys/param.h> 3239571Sbostic #include <sys/signal.h> 331169Sbill #include <sys/stat.h> 3439571Sbostic #include <sys/file.h> 3537937Sbostic #include <sys/time.h> 361169Sbill #include <utmp.h> 3737937Sbostic #include <ctype.h> 3839571Sbostic #include <pwd.h> 3939571Sbostic #include <stdio.h> 4039571Sbostic #include <strings.h> 411169Sbill 4239576Sbostic extern int errno; 431169Sbill 441169Sbill main(argc, argv) 456202Sroot int argc; 4639571Sbostic char **argv; 471169Sbill { 4839571Sbostic register char *cp; 4939576Sbostic time_t atime; 5039576Sbostic uid_t myuid; 5139571Sbostic int msgsok, myttyfd; 52*39620Sleres char tty[MAXPATHLEN], *mytty, *ttyname(); 5339571Sbostic void done(); 541169Sbill 5539576Sbostic /* check that sender has write enabled */ 5639571Sbostic if (isatty(fileno(stdin))) 5739571Sbostic myttyfd = fileno(stdin); 5839571Sbostic else if (isatty(fileno(stdout))) 5939571Sbostic myttyfd = fileno(stdout); 6039571Sbostic else if (isatty(fileno(stderr))) 6139571Sbostic myttyfd = fileno(stderr); 6239571Sbostic else { 6339571Sbostic (void)fprintf(stderr, "write: can't find your tty\n"); 641169Sbill exit(1); 651169Sbill } 6639571Sbostic if (!(mytty = ttyname(myttyfd))) { 6739571Sbostic (void)fprintf(stderr, "write: can't find your tty's name\n"); 681169Sbill exit(1); 691169Sbill } 7039571Sbostic if (cp = rindex(mytty, '/')) 7139571Sbostic mytty = cp + 1; 7239571Sbostic if (term_chk(mytty, &msgsok, &atime, 1)) 736202Sroot exit(1); 7439571Sbostic if (!msgsok) { 7539571Sbostic (void)fprintf(stderr, 7639571Sbostic "write: you have write permission turned off.\n"); 7739571Sbostic exit(1); 781881Serics } 7939571Sbostic 8039576Sbostic myuid = getuid(); 8139571Sbostic 8239571Sbostic /* check args */ 8339571Sbostic switch (argc) { 8439571Sbostic case 2: 8539576Sbostic search_utmp(argv[1], tty, mytty, myuid); 8639576Sbostic do_write(tty, mytty, myuid); 8739571Sbostic break; 8839571Sbostic case 3: 8939571Sbostic if (!strncmp(argv[2], "/dev/", 5)) 9039571Sbostic argv[2] += 5; 9139571Sbostic if (utmp_chk(argv[1], argv[2])) { 9239571Sbostic (void)fprintf(stderr, 9339571Sbostic "write: %s is not logged in on %s.\n", 9439571Sbostic argv[1], argv[2]); 9526068Skarels exit(1); 961169Sbill } 9739571Sbostic if (term_chk(argv[2], &msgsok, &atime, 1)) 9839571Sbostic exit(1); 9939576Sbostic if (myuid && !msgsok) { 10039571Sbostic (void)fprintf(stderr, 10139571Sbostic "write: %s has messages disabled on %s\n", 10239571Sbostic argv[1], argv[2]); 10339571Sbostic exit(1); 1041169Sbill } 10539576Sbostic do_write(argv[2], mytty, myuid); 10639571Sbostic break; 10739571Sbostic default: 10839571Sbostic (void)fprintf(stderr, "usage: write user [tty]\n"); 1091169Sbill exit(1); 1101169Sbill } 11139571Sbostic done(); 11239571Sbostic /* NOTREACHED */ 11339571Sbostic } 11439571Sbostic 11539571Sbostic /* 11639571Sbostic * utmp_chk - checks that the given user is actually logged in on 11739571Sbostic * the given tty 11839571Sbostic */ 11939571Sbostic utmp_chk(user, tty) 12039571Sbostic char *user, *tty; 12139571Sbostic { 12239571Sbostic struct utmp u; 12339571Sbostic int ufd; 12439571Sbostic 12539571Sbostic if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 12639571Sbostic return(0); /* ignore error, shouldn't happen anyway */ 12739571Sbostic 12839571Sbostic while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 12939571Sbostic if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && 13039571Sbostic strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { 13139571Sbostic (void)close(ufd); 13239571Sbostic return(0); 1331169Sbill } 13422608Sserge 13539571Sbostic (void)close(ufd); 13639571Sbostic return(1); 13739571Sbostic } 13822608Sserge 13939571Sbostic /* 14039571Sbostic * search_utmp - search utmp for the "best" terminal to write to 14139571Sbostic * 14239571Sbostic * Ignores terminals with messages disabled, and of the rest, returns 14339571Sbostic * the one with the most recent access time. Returns as value the number 14439571Sbostic * of the user's terminals with messages enabled, or -1 if the user is 14539571Sbostic * not logged in at all. 14639571Sbostic * 14739571Sbostic * Special case for writing to yourself - ignore the terminal you're 14839571Sbostic * writing from, unless that's the only terminal with messages enabled. 14939571Sbostic */ 15039576Sbostic search_utmp(user, tty, mytty, myuid) 15139571Sbostic char *user, *tty, *mytty; 15239576Sbostic uid_t myuid; 15339571Sbostic { 15439571Sbostic struct utmp u; 15539571Sbostic time_t bestatime, atime; 15639571Sbostic int ufd, nloggedttys, nttys, msgsok, user_is_me; 15739571Sbostic char atty[UT_LINESIZE + 1]; 15822608Sserge 15939571Sbostic if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) { 16039571Sbostic perror("utmp"); 16139571Sbostic exit(1); 16239571Sbostic } 16322608Sserge 16439571Sbostic nloggedttys = nttys = 0; 16539571Sbostic bestatime = 0; 16639571Sbostic user_is_me = 0; 16739571Sbostic while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 16839571Sbostic if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { 16939571Sbostic ++nloggedttys; 17039576Sbostic (void)strncpy(atty, u.ut_line, UT_LINESIZE); 17139576Sbostic atty[UT_LINESIZE] = '\0'; 17239571Sbostic if (term_chk(atty, &msgsok, &atime, 0)) 17339571Sbostic continue; /* bad term? skip */ 17439576Sbostic if (myuid && !msgsok) 17539571Sbostic continue; /* skip ttys with msgs off */ 17639571Sbostic if (strcmp(atty, mytty) == 0) { 17739571Sbostic user_is_me = 1; 17839571Sbostic continue; /* don't write to yourself */ 17922608Sserge } 18039571Sbostic ++nttys; 18139571Sbostic if (atime > bestatime) { 18239571Sbostic bestatime = atime; 18339571Sbostic (void)strcpy(tty, atty); 18439571Sbostic } 1857401Skre } 18639571Sbostic 18739571Sbostic (void)close(ufd); 18839571Sbostic if (nloggedttys == 0) { 18939571Sbostic (void)fprintf(stderr, "write: %s is not logged in\n", user); 19039571Sbostic exit(1); 1911169Sbill } 19239571Sbostic if (nttys == 0) { 19339571Sbostic if (user_is_me) { /* ok, so write to yourself! */ 19439571Sbostic (void)strcpy(tty, mytty); 19539571Sbostic return; 19639571Sbostic } 19739571Sbostic (void)fprintf(stderr, 19839571Sbostic "write: %s has messages disabled\n", user); 19939571Sbostic exit(1); 20039571Sbostic } else if (nttys > 1) { 20139571Sbostic (void)fprintf(stderr, 20239571Sbostic "write: %s is logged in more than once; writing to %s\n", 20339571Sbostic user, tty); 20439571Sbostic } 2051169Sbill } 2061169Sbill 20739571Sbostic /* 20839571Sbostic * term_chk - check that a terminal exists, and get the message bit 20939571Sbostic * and the access time 21039571Sbostic */ 21139571Sbostic term_chk(tty, msgsokP, atimeP, showerror) 21239571Sbostic char *tty; 21339571Sbostic int *msgsokP, showerror; 21439571Sbostic time_t *atimeP; 2151169Sbill { 21639571Sbostic struct stat s; 21739571Sbostic char path[MAXPATHLEN]; 2181169Sbill 21939571Sbostic (void)sprintf(path, "/dev/%s", tty); 22039571Sbostic if (stat(path, &s) < 0) { 22139571Sbostic if (showerror) 22239576Sbostic (void)fprintf(stderr, 22339576Sbostic "write: %s: %s\n", path, strerror(errno)); 22439571Sbostic return(1); 22539571Sbostic } 22639571Sbostic *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ 22739571Sbostic *atimeP = s.st_atime; 22839571Sbostic return(0); 2291169Sbill } 2301169Sbill 23139571Sbostic /* 23239571Sbostic * do_write - actually make the connection 23339571Sbostic */ 23439576Sbostic do_write(tty, mytty, myuid) 23539571Sbostic char *tty, *mytty; 23639576Sbostic uid_t myuid; 2371169Sbill { 23839571Sbostic register char *login, *nows; 23939571Sbostic register struct passwd *pwd; 24039571Sbostic time_t now, time(); 241*39620Sleres char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512]; 24239571Sbostic void done(); 2431169Sbill 24439619Sleres /* Determine our login name before the we reopen() stdout */ 24539619Sleres if ((login = getlogin()) == NULL) 24639619Sleres if (pwd = getpwuid(myuid)) 24739619Sleres login = pwd->pw_name; 24839619Sleres else 24939619Sleres login = "???"; 25039619Sleres 25139571Sbostic (void)sprintf(path, "/dev/%s", tty); 25239571Sbostic if ((freopen(path, "w", stdout)) == NULL) { 25339619Sleres (void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno)); 25439571Sbostic exit(1); 25539571Sbostic } 25639571Sbostic 25739571Sbostic (void)signal(SIGINT, done); 25839576Sbostic (void)signal(SIGHUP, done); 25939571Sbostic 26039576Sbostic /* print greeting */ 26139571Sbostic if (gethostname(host, sizeof(host)) < 0) 26239571Sbostic (void)strcpy(host, "???"); 26339571Sbostic now = time((time_t *)NULL); 26439571Sbostic nows = ctime(&now); 26539571Sbostic nows[16] = '\0'; 26639571Sbostic (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", 26739576Sbostic login, host, mytty, nows + 11); 26839571Sbostic 26939571Sbostic while (fgets(line, sizeof(line), stdin) != NULL) 27039576Sbostic wr_fputs(line); 27139571Sbostic } 27239571Sbostic 27339571Sbostic /* 27439571Sbostic * done - cleanup and exit 27539571Sbostic */ 27639571Sbostic void 27739571Sbostic done() 27839571Sbostic { 27939571Sbostic (void)printf("EOF\r\n"); 2801169Sbill exit(0); 2811169Sbill } 2821169Sbill 28339571Sbostic /* 28439576Sbostic * wr_fputs - like fputs(), but makes control characters visible and 28539571Sbostic * turns \n into \r\n 28639571Sbostic */ 28739576Sbostic wr_fputs(s) 28839571Sbostic register char *s; 2891169Sbill { 29039571Sbostic register char c; 2911169Sbill 29239571Sbostic #define PUTC(c) if (putchar(c) == EOF) goto err; 29339571Sbostic 29439571Sbostic for (; *s != '\0'; ++s) { 29539571Sbostic c = toascii(*s); 29639571Sbostic if (c == '\n') { 29739571Sbostic PUTC('\r'); 29839571Sbostic PUTC('\n'); 29939571Sbostic } else if (!isprint(c) && !isspace(c) && c != '\007') { 30039571Sbostic PUTC('^'); 30139571Sbostic PUTC(c^0x40); /* DEL to ?, others to alpha */ 30239571Sbostic } else 30339571Sbostic PUTC(c); 3041169Sbill } 30539571Sbostic return; 3061169Sbill 30739571Sbostic err: (void)fprintf(stderr, "write: %s\n", strerror(errno)); 30839571Sbostic exit(1); 30939571Sbostic #undef PUTC 3101169Sbill } 311