11169Sbill /*
262457Sbostic * Copyright (c) 1989, 1993
362457Sbostic * 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
1262457Sbostic static char copyright[] =
1362457Sbostic "@(#) Copyright (c) 1989, 1993\n\
1462457Sbostic The Regents of the University of California. All rights reserved.\n";
1539571Sbostic #endif /* not lint */
1639571Sbostic
1739571Sbostic #ifndef lint
18*68968Sbostic static char sccsid[] = "@(#)write.c 8.2 (Berkeley) 04/27/95";
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>
26*68968Sbostic
27*68968Sbostic #include <err.h>
281169Sbill #include <utmp.h>
2937937Sbostic #include <ctype.h>
3039571Sbostic #include <pwd.h>
3139571Sbostic #include <stdio.h>
3242044Sbostic #include <string.h>
331169Sbill
3439576Sbostic extern int errno;
351169Sbill
main(argc,argv)361169Sbill main(argc, argv)
376202Sroot int argc;
3839571Sbostic char **argv;
391169Sbill {
4039571Sbostic register char *cp;
4139576Sbostic time_t atime;
4239576Sbostic uid_t myuid;
4339571Sbostic int msgsok, myttyfd;
4439620Sleres char tty[MAXPATHLEN], *mytty, *ttyname();
4539571Sbostic void done();
461169Sbill
4739576Sbostic /* check that sender has write enabled */
4839571Sbostic if (isatty(fileno(stdin)))
4939571Sbostic myttyfd = fileno(stdin);
5039571Sbostic else if (isatty(fileno(stdout)))
5139571Sbostic myttyfd = fileno(stdout);
5239571Sbostic else if (isatty(fileno(stderr)))
5339571Sbostic myttyfd = fileno(stderr);
54*68968Sbostic else
55*68968Sbostic errx(1, "can't find your tty");
56*68968Sbostic if (!(mytty = ttyname(myttyfd)))
57*68968Sbostic errx(1, "can't find your tty's name");
58*68968Sbostic if (cp = strrchr(mytty, '/'))
5939571Sbostic mytty = cp + 1;
6039571Sbostic if (term_chk(mytty, &msgsok, &atime, 1))
616202Sroot exit(1);
62*68968Sbostic if (!msgsok)
63*68968Sbostic errx(1, "you have write permission turned off");
6439571Sbostic
6539576Sbostic myuid = getuid();
6639571Sbostic
6739571Sbostic /* check args */
6839571Sbostic switch (argc) {
6939571Sbostic case 2:
7039576Sbostic search_utmp(argv[1], tty, mytty, myuid);
7139576Sbostic do_write(tty, mytty, myuid);
7239571Sbostic break;
7339571Sbostic case 3:
7439571Sbostic if (!strncmp(argv[2], "/dev/", 5))
7539571Sbostic argv[2] += 5;
76*68968Sbostic if (utmp_chk(argv[1], argv[2]))
77*68968Sbostic errx(1, "%s is not logged in on %s",
7839571Sbostic argv[1], argv[2]);
7939571Sbostic if (term_chk(argv[2], &msgsok, &atime, 1))
8039571Sbostic exit(1);
81*68968Sbostic if (myuid && !msgsok)
82*68968Sbostic errx(1, "%s has messages disabled on %s",
8339571Sbostic argv[1], argv[2]);
8439576Sbostic do_write(argv[2], mytty, myuid);
8539571Sbostic break;
8639571Sbostic default:
8739571Sbostic (void)fprintf(stderr, "usage: write user [tty]\n");
881169Sbill exit(1);
891169Sbill }
9039571Sbostic done();
9139571Sbostic /* NOTREACHED */
9239571Sbostic }
9339571Sbostic
9439571Sbostic /*
9539571Sbostic * utmp_chk - checks that the given user is actually logged in on
9639571Sbostic * the given tty
9739571Sbostic */
utmp_chk(user,tty)9839571Sbostic utmp_chk(user, tty)
9939571Sbostic char *user, *tty;
10039571Sbostic {
10139571Sbostic struct utmp u;
10239571Sbostic int ufd;
10339571Sbostic
10439571Sbostic if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
10539571Sbostic return(0); /* ignore error, shouldn't happen anyway */
10639571Sbostic
10739571Sbostic while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
10839571Sbostic if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 &&
10939571Sbostic strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) {
11039571Sbostic (void)close(ufd);
11139571Sbostic return(0);
1121169Sbill }
11322608Sserge
11439571Sbostic (void)close(ufd);
11539571Sbostic return(1);
11639571Sbostic }
11722608Sserge
11839571Sbostic /*
11939571Sbostic * search_utmp - search utmp for the "best" terminal to write to
12039571Sbostic *
12139571Sbostic * Ignores terminals with messages disabled, and of the rest, returns
12239571Sbostic * the one with the most recent access time. Returns as value the number
12339571Sbostic * of the user's terminals with messages enabled, or -1 if the user is
12439571Sbostic * not logged in at all.
12539571Sbostic *
12639571Sbostic * Special case for writing to yourself - ignore the terminal you're
12739571Sbostic * writing from, unless that's the only terminal with messages enabled.
12839571Sbostic */
search_utmp(user,tty,mytty,myuid)12939576Sbostic search_utmp(user, tty, mytty, myuid)
13039571Sbostic char *user, *tty, *mytty;
13139576Sbostic uid_t myuid;
13239571Sbostic {
13339571Sbostic struct utmp u;
13439571Sbostic time_t bestatime, atime;
13539571Sbostic int ufd, nloggedttys, nttys, msgsok, user_is_me;
13639571Sbostic char atty[UT_LINESIZE + 1];
13722608Sserge
138*68968Sbostic if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0)
139*68968Sbostic err(1, "%s", _PATH_UTMP);
14022608Sserge
14139571Sbostic nloggedttys = nttys = 0;
14239571Sbostic bestatime = 0;
14339571Sbostic user_is_me = 0;
14439571Sbostic while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u))
14539571Sbostic if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) {
14639571Sbostic ++nloggedttys;
14739576Sbostic (void)strncpy(atty, u.ut_line, UT_LINESIZE);
14839576Sbostic atty[UT_LINESIZE] = '\0';
14939571Sbostic if (term_chk(atty, &msgsok, &atime, 0))
15039571Sbostic continue; /* bad term? skip */
15139576Sbostic if (myuid && !msgsok)
15239571Sbostic continue; /* skip ttys with msgs off */
15339571Sbostic if (strcmp(atty, mytty) == 0) {
15439571Sbostic user_is_me = 1;
15539571Sbostic continue; /* don't write to yourself */
15622608Sserge }
15739571Sbostic ++nttys;
15839571Sbostic if (atime > bestatime) {
15939571Sbostic bestatime = atime;
16039571Sbostic (void)strcpy(tty, atty);
16139571Sbostic }
1627401Skre }
16339571Sbostic
16439571Sbostic (void)close(ufd);
165*68968Sbostic if (nloggedttys == 0)
166*68968Sbostic errx(1, "%s is not logged in", user);
16739571Sbostic if (nttys == 0) {
16839571Sbostic if (user_is_me) { /* ok, so write to yourself! */
16939571Sbostic (void)strcpy(tty, mytty);
17039571Sbostic return;
17139571Sbostic }
172*68968Sbostic errx(1, "%s has messages disabled", user);
173*68968Sbostic } else if (nttys > 1)
174*68968Sbostic warnx("%s is logged in more than once; writing to %s",
17539571Sbostic user, tty);
1761169Sbill }
1771169Sbill
17839571Sbostic /*
17939571Sbostic * term_chk - check that a terminal exists, and get the message bit
18039571Sbostic * and the access time
18139571Sbostic */
term_chk(tty,msgsokP,atimeP,showerror)18239571Sbostic term_chk(tty, msgsokP, atimeP, showerror)
18339571Sbostic char *tty;
18439571Sbostic int *msgsokP, showerror;
18539571Sbostic time_t *atimeP;
1861169Sbill {
18739571Sbostic struct stat s;
18839571Sbostic char path[MAXPATHLEN];
1891169Sbill
19039571Sbostic (void)sprintf(path, "/dev/%s", tty);
19139571Sbostic if (stat(path, &s) < 0) {
19239571Sbostic if (showerror)
193*68968Sbostic warn("%s", path);
19439571Sbostic return(1);
19539571Sbostic }
19639571Sbostic *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */
19739571Sbostic *atimeP = s.st_atime;
19839571Sbostic return(0);
1991169Sbill }
2001169Sbill
20139571Sbostic /*
20239571Sbostic * do_write - actually make the connection
20339571Sbostic */
do_write(tty,mytty,myuid)20439576Sbostic do_write(tty, mytty, myuid)
20539571Sbostic char *tty, *mytty;
20639576Sbostic uid_t myuid;
2071169Sbill {
20839571Sbostic register char *login, *nows;
20939571Sbostic register struct passwd *pwd;
21039571Sbostic time_t now, time();
21139620Sleres char *getlogin(), path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512];
21239571Sbostic void done();
2131169Sbill
21439619Sleres /* Determine our login name before the we reopen() stdout */
21539619Sleres if ((login = getlogin()) == NULL)
21639619Sleres if (pwd = getpwuid(myuid))
21739619Sleres login = pwd->pw_name;
21839619Sleres else
21939619Sleres login = "???";
22039619Sleres
22139571Sbostic (void)sprintf(path, "/dev/%s", tty);
222*68968Sbostic if ((freopen(path, "w", stdout)) == NULL)
223*68968Sbostic err(1, "%s", path);
22439571Sbostic
22539571Sbostic (void)signal(SIGINT, done);
22639576Sbostic (void)signal(SIGHUP, done);
22739571Sbostic
22839576Sbostic /* print greeting */
22939571Sbostic if (gethostname(host, sizeof(host)) < 0)
23039571Sbostic (void)strcpy(host, "???");
23139571Sbostic now = time((time_t *)NULL);
23239571Sbostic nows = ctime(&now);
23339571Sbostic nows[16] = '\0';
23439571Sbostic (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n",
23539576Sbostic login, host, mytty, nows + 11);
23639571Sbostic
23739571Sbostic while (fgets(line, sizeof(line), stdin) != NULL)
23839576Sbostic wr_fputs(line);
23939571Sbostic }
24039571Sbostic
24139571Sbostic /*
24239571Sbostic * done - cleanup and exit
24339571Sbostic */
24439571Sbostic void
done()24539571Sbostic done()
24639571Sbostic {
24739571Sbostic (void)printf("EOF\r\n");
2481169Sbill exit(0);
2491169Sbill }
2501169Sbill
25139571Sbostic /*
25239576Sbostic * wr_fputs - like fputs(), but makes control characters visible and
25339571Sbostic * turns \n into \r\n
25439571Sbostic */
wr_fputs(s)25539576Sbostic wr_fputs(s)
25639571Sbostic register char *s;
2571169Sbill {
25839571Sbostic register char c;
2591169Sbill
26039571Sbostic #define PUTC(c) if (putchar(c) == EOF) goto err;
26139571Sbostic
26239571Sbostic for (; *s != '\0'; ++s) {
26339571Sbostic c = toascii(*s);
26439571Sbostic if (c == '\n') {
26539571Sbostic PUTC('\r');
26639571Sbostic PUTC('\n');
26739571Sbostic } else if (!isprint(c) && !isspace(c) && c != '\007') {
26839571Sbostic PUTC('^');
26939571Sbostic PUTC(c^0x40); /* DEL to ?, others to alpha */
27039571Sbostic } else
27139571Sbostic PUTC(c);
2721169Sbill }
27339571Sbostic return;
2741169Sbill
275*68968Sbostic err: err(1, NULL);
27639571Sbostic #undef PUTC
2771169Sbill }
278