11169Sbill /* 2*62457Sbostic * Copyright (c) 1989, 1993 3*62457Sbostic * The Regents of the University of California. 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 * 842788Sbostic * %sccs.include.redist.c% 91169Sbill */ 101169Sbill 1139571Sbostic #ifndef lint 12*62457Sbostic static char copyright[] = 13*62457Sbostic "@(#) Copyright (c) 1989, 1993\n\ 14*62457Sbostic The Regents of the University of California. All rights reserved.\n"; 1539571Sbostic #endif /* not lint */ 1639571Sbostic 1739571Sbostic #ifndef lint 18*62457Sbostic static char sccsid[] = "@(#)write.c 8.1 (Berkeley) 06/06/93"; 1939571Sbostic #endif /* not lint */ 2039571Sbostic 2139571Sbostic #include <sys/param.h> 2239571Sbostic #include <sys/signal.h> 231169Sbill #include <sys/stat.h> 2439571Sbostic #include <sys/file.h> 2537937Sbostic #include <sys/time.h> 261169Sbill #include <utmp.h> 2737937Sbostic #include <ctype.h> 2839571Sbostic #include <pwd.h> 2939571Sbostic #include <stdio.h> 3042044Sbostic #include <string.h> 311169Sbill 3239576Sbostic extern int errno; 331169Sbill 341169Sbill main(argc, argv) 356202Sroot int argc; 3639571Sbostic char **argv; 371169Sbill { 3839571Sbostic register char *cp; 3939576Sbostic time_t atime; 4039576Sbostic uid_t myuid; 4139571Sbostic int msgsok, myttyfd; 4239620Sleres char tty[MAXPATHLEN], *mytty, *ttyname(); 4339571Sbostic void done(); 441169Sbill 4539576Sbostic /* check that sender has write enabled */ 4639571Sbostic if (isatty(fileno(stdin))) 4739571Sbostic myttyfd = fileno(stdin); 4839571Sbostic else if (isatty(fileno(stdout))) 4939571Sbostic myttyfd = fileno(stdout); 5039571Sbostic else if (isatty(fileno(stderr))) 5139571Sbostic myttyfd = fileno(stderr); 5239571Sbostic else { 5339571Sbostic (void)fprintf(stderr, "write: can't find your tty\n"); 541169Sbill exit(1); 551169Sbill } 5639571Sbostic if (!(mytty = ttyname(myttyfd))) { 5739571Sbostic (void)fprintf(stderr, "write: can't find your tty's name\n"); 581169Sbill exit(1); 591169Sbill } 6039571Sbostic if (cp = rindex(mytty, '/')) 6139571Sbostic mytty = cp + 1; 6239571Sbostic if (term_chk(mytty, &msgsok, &atime, 1)) 636202Sroot exit(1); 6439571Sbostic if (!msgsok) { 6539571Sbostic (void)fprintf(stderr, 6639571Sbostic "write: you have write permission turned off.\n"); 6739571Sbostic exit(1); 681881Serics } 6939571Sbostic 7039576Sbostic myuid = getuid(); 7139571Sbostic 7239571Sbostic /* check args */ 7339571Sbostic switch (argc) { 7439571Sbostic case 2: 7539576Sbostic search_utmp(argv[1], tty, mytty, myuid); 7639576Sbostic do_write(tty, mytty, myuid); 7739571Sbostic break; 7839571Sbostic case 3: 7939571Sbostic if (!strncmp(argv[2], "/dev/", 5)) 8039571Sbostic argv[2] += 5; 8139571Sbostic if (utmp_chk(argv[1], argv[2])) { 8239571Sbostic (void)fprintf(stderr, 8339571Sbostic "write: %s is not logged in on %s.\n", 8439571Sbostic argv[1], argv[2]); 8526068Skarels exit(1); 861169Sbill } 8739571Sbostic if (term_chk(argv[2], &msgsok, &atime, 1)) 8839571Sbostic exit(1); 8939576Sbostic if (myuid && !msgsok) { 9039571Sbostic (void)fprintf(stderr, 9139571Sbostic "write: %s has messages disabled on %s\n", 9239571Sbostic argv[1], argv[2]); 9339571Sbostic exit(1); 941169Sbill } 9539576Sbostic do_write(argv[2], mytty, myuid); 9639571Sbostic break; 9739571Sbostic default: 9839571Sbostic (void)fprintf(stderr, "usage: write user [tty]\n"); 991169Sbill exit(1); 1001169Sbill } 10139571Sbostic done(); 10239571Sbostic /* NOTREACHED */ 10339571Sbostic } 10439571Sbostic 10539571Sbostic /* 10639571Sbostic * utmp_chk - checks that the given user is actually logged in on 10739571Sbostic * the given tty 10839571Sbostic */ 10939571Sbostic utmp_chk(user, tty) 11039571Sbostic char *user, *tty; 11139571Sbostic { 11239571Sbostic struct utmp u; 11339571Sbostic int ufd; 11439571Sbostic 11539571Sbostic if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 11639571Sbostic return(0); /* ignore error, shouldn't happen anyway */ 11739571Sbostic 11839571Sbostic while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 11939571Sbostic if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && 12039571Sbostic strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { 12139571Sbostic (void)close(ufd); 12239571Sbostic return(0); 1231169Sbill } 12422608Sserge 12539571Sbostic (void)close(ufd); 12639571Sbostic return(1); 12739571Sbostic } 12822608Sserge 12939571Sbostic /* 13039571Sbostic * search_utmp - search utmp for the "best" terminal to write to 13139571Sbostic * 13239571Sbostic * Ignores terminals with messages disabled, and of the rest, returns 13339571Sbostic * the one with the most recent access time. Returns as value the number 13439571Sbostic * of the user's terminals with messages enabled, or -1 if the user is 13539571Sbostic * not logged in at all. 13639571Sbostic * 13739571Sbostic * Special case for writing to yourself - ignore the terminal you're 13839571Sbostic * writing from, unless that's the only terminal with messages enabled. 13939571Sbostic */ 14039576Sbostic search_utmp(user, tty, mytty, myuid) 14139571Sbostic char *user, *tty, *mytty; 14239576Sbostic uid_t myuid; 14339571Sbostic { 14439571Sbostic struct utmp u; 14539571Sbostic time_t bestatime, atime; 14639571Sbostic int ufd, nloggedttys, nttys, msgsok, user_is_me; 14739571Sbostic char atty[UT_LINESIZE + 1]; 14822608Sserge 14939571Sbostic if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) { 15039571Sbostic perror("utmp"); 15139571Sbostic exit(1); 15239571Sbostic } 15322608Sserge 15439571Sbostic nloggedttys = nttys = 0; 15539571Sbostic bestatime = 0; 15639571Sbostic user_is_me = 0; 15739571Sbostic while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 15839571Sbostic if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { 15939571Sbostic ++nloggedttys; 16039576Sbostic (void)strncpy(atty, u.ut_line, UT_LINESIZE); 16139576Sbostic atty[UT_LINESIZE] = '\0'; 16239571Sbostic if (term_chk(atty, &msgsok, &atime, 0)) 16339571Sbostic continue; /* bad term? skip */ 16439576Sbostic if (myuid && !msgsok) 16539571Sbostic continue; /* skip ttys with msgs off */ 16639571Sbostic if (strcmp(atty, mytty) == 0) { 16739571Sbostic user_is_me = 1; 16839571Sbostic continue; /* don't write to yourself */ 16922608Sserge } 17039571Sbostic ++nttys; 17139571Sbostic if (atime > bestatime) { 17239571Sbostic bestatime = atime; 17339571Sbostic (void)strcpy(tty, atty); 17439571Sbostic } 1757401Skre } 17639571Sbostic 17739571Sbostic (void)close(ufd); 17839571Sbostic if (nloggedttys == 0) { 17939571Sbostic (void)fprintf(stderr, "write: %s is not logged in\n", user); 18039571Sbostic exit(1); 1811169Sbill } 18239571Sbostic if (nttys == 0) { 18339571Sbostic if (user_is_me) { /* ok, so write to yourself! */ 18439571Sbostic (void)strcpy(tty, mytty); 18539571Sbostic return; 18639571Sbostic } 18739571Sbostic (void)fprintf(stderr, 18839571Sbostic "write: %s has messages disabled\n", user); 18939571Sbostic exit(1); 19039571Sbostic } else if (nttys > 1) { 19139571Sbostic (void)fprintf(stderr, 19239571Sbostic "write: %s is logged in more than once; writing to %s\n", 19339571Sbostic user, tty); 19439571Sbostic } 1951169Sbill } 1961169Sbill 19739571Sbostic /* 19839571Sbostic * term_chk - check that a terminal exists, and get the message bit 19939571Sbostic * and the access time 20039571Sbostic */ 20139571Sbostic term_chk(tty, msgsokP, atimeP, showerror) 20239571Sbostic char *tty; 20339571Sbostic int *msgsokP, showerror; 20439571Sbostic time_t *atimeP; 2051169Sbill { 20639571Sbostic struct stat s; 20739571Sbostic char path[MAXPATHLEN]; 2081169Sbill 20939571Sbostic (void)sprintf(path, "/dev/%s", tty); 21039571Sbostic if (stat(path, &s) < 0) { 21139571Sbostic if (showerror) 21239576Sbostic (void)fprintf(stderr, 21339576Sbostic "write: %s: %s\n", path, strerror(errno)); 21439571Sbostic return(1); 21539571Sbostic } 21639571Sbostic *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ 21739571Sbostic *atimeP = s.st_atime; 21839571Sbostic return(0); 2191169Sbill } 2201169Sbill 22139571Sbostic /* 22239571Sbostic * do_write - actually make the connection 22339571Sbostic */ 22439576Sbostic do_write(tty, mytty, myuid) 22539571Sbostic char *tty, *mytty; 22639576Sbostic uid_t myuid; 2271169Sbill { 22839571Sbostic register char *login, *nows; 22939571Sbostic register struct passwd *pwd; 23039571Sbostic time_t now, time(); 23139620Sleres char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512]; 23239571Sbostic void done(); 2331169Sbill 23439619Sleres /* Determine our login name before the we reopen() stdout */ 23539619Sleres if ((login = getlogin()) == NULL) 23639619Sleres if (pwd = getpwuid(myuid)) 23739619Sleres login = pwd->pw_name; 23839619Sleres else 23939619Sleres login = "???"; 24039619Sleres 24139571Sbostic (void)sprintf(path, "/dev/%s", tty); 24239571Sbostic if ((freopen(path, "w", stdout)) == NULL) { 24339619Sleres (void)fprintf(stderr, "write: %s: %s\n", path, strerror(errno)); 24439571Sbostic exit(1); 24539571Sbostic } 24639571Sbostic 24739571Sbostic (void)signal(SIGINT, done); 24839576Sbostic (void)signal(SIGHUP, done); 24939571Sbostic 25039576Sbostic /* print greeting */ 25139571Sbostic if (gethostname(host, sizeof(host)) < 0) 25239571Sbostic (void)strcpy(host, "???"); 25339571Sbostic now = time((time_t *)NULL); 25439571Sbostic nows = ctime(&now); 25539571Sbostic nows[16] = '\0'; 25639571Sbostic (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", 25739576Sbostic login, host, mytty, nows + 11); 25839571Sbostic 25939571Sbostic while (fgets(line, sizeof(line), stdin) != NULL) 26039576Sbostic wr_fputs(line); 26139571Sbostic } 26239571Sbostic 26339571Sbostic /* 26439571Sbostic * done - cleanup and exit 26539571Sbostic */ 26639571Sbostic void 26739571Sbostic done() 26839571Sbostic { 26939571Sbostic (void)printf("EOF\r\n"); 2701169Sbill exit(0); 2711169Sbill } 2721169Sbill 27339571Sbostic /* 27439576Sbostic * wr_fputs - like fputs(), but makes control characters visible and 27539571Sbostic * turns \n into \r\n 27639571Sbostic */ 27739576Sbostic wr_fputs(s) 27839571Sbostic register char *s; 2791169Sbill { 28039571Sbostic register char c; 2811169Sbill 28239571Sbostic #define PUTC(c) if (putchar(c) == EOF) goto err; 28339571Sbostic 28439571Sbostic for (; *s != '\0'; ++s) { 28539571Sbostic c = toascii(*s); 28639571Sbostic if (c == '\n') { 28739571Sbostic PUTC('\r'); 28839571Sbostic PUTC('\n'); 28939571Sbostic } else if (!isprint(c) && !isspace(c) && c != '\007') { 29039571Sbostic PUTC('^'); 29139571Sbostic PUTC(c^0x40); /* DEL to ?, others to alpha */ 29239571Sbostic } else 29339571Sbostic PUTC(c); 2941169Sbill } 29539571Sbostic return; 2961169Sbill 29739571Sbostic err: (void)fprintf(stderr, "write: %s\n", strerror(errno)); 29839571Sbostic exit(1); 29939571Sbostic #undef PUTC 3001169Sbill } 301