11169Sbill /* 2*39571Sbostic * Copyright (c) 1989 The Regents of the University of California. 3*39571Sbostic * All rights reserved. 4*39571Sbostic * 5*39571Sbostic * This code is derived from software contributed to Berkeley by 6*39571Sbostic * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. 7*39571Sbostic * 8*39571Sbostic * Redistribution and use in source and binary forms are permitted 9*39571Sbostic * provided that the above copyright notice and this paragraph are 10*39571Sbostic * duplicated in all such forms and that any documentation, 11*39571Sbostic * advertising materials, and other materials related to such 12*39571Sbostic * distribution and use acknowledge that the software was developed 13*39571Sbostic * by the University of California, Berkeley. The name of the 14*39571Sbostic * University may not be used to endorse or promote products derived 15*39571Sbostic * from this software without specific prior written permission. 16*39571Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 17*39571Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 18*39571Sbostic * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 191169Sbill */ 201169Sbill 21*39571Sbostic #ifndef lint 22*39571Sbostic char copyright[] = 23*39571Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 24*39571Sbostic All rights reserved.\n"; 25*39571Sbostic #endif /* not lint */ 26*39571Sbostic 27*39571Sbostic #ifndef lint 28*39571Sbostic static char sccsid[] = "@(#)write.c 4.16 (Berkeley) 11/21/89"; 29*39571Sbostic #endif /* not lint */ 30*39571Sbostic 31*39571Sbostic #include <sys/param.h> 32*39571Sbostic #include <sys/signal.h> 331169Sbill #include <sys/stat.h> 34*39571Sbostic #include <sys/file.h> 3537937Sbostic #include <sys/time.h> 361169Sbill #include <utmp.h> 3737937Sbostic #include <ctype.h> 3837937Sbostic #include <paths.h> 39*39571Sbostic #include <pwd.h> 40*39571Sbostic #include <stdio.h> 41*39571Sbostic #include <strings.h> 421169Sbill 43*39571Sbostic #define STRCPY(s1, s2) \ 44*39571Sbostic { (void)strncpy(s1, s2, sizeof(s1)); s1[sizeof(s1) - 1] = '\0'; } 451169Sbill 46*39571Sbostic int uid; /* myuid */ 471169Sbill 481169Sbill main(argc, argv) 496202Sroot int argc; 50*39571Sbostic char **argv; 511169Sbill { 52*39571Sbostic extern int errno; 53*39571Sbostic register char *cp; 54*39571Sbostic char tty[MAXPATHLEN]; 55*39571Sbostic int msgsok, myttyfd; 56*39571Sbostic time_t atime; 57*39571Sbostic char *mytty, *getlogin(), *ttyname(); 58*39571Sbostic void done(); 591169Sbill 60*39571Sbostic /* check that sender has write enabled. */ 61*39571Sbostic if (isatty(fileno(stdin))) 62*39571Sbostic myttyfd = fileno(stdin); 63*39571Sbostic else if (isatty(fileno(stdout))) 64*39571Sbostic myttyfd = fileno(stdout); 65*39571Sbostic else if (isatty(fileno(stderr))) 66*39571Sbostic myttyfd = fileno(stderr); 67*39571Sbostic else { 68*39571Sbostic (void)fprintf(stderr, "write: can't find your tty\n"); 691169Sbill exit(1); 701169Sbill } 71*39571Sbostic if (!(mytty = ttyname(myttyfd))) { 72*39571Sbostic (void)fprintf(stderr, "write: can't find your tty's name\n"); 731169Sbill exit(1); 741169Sbill } 75*39571Sbostic if (cp = rindex(mytty, '/')) 76*39571Sbostic mytty = cp + 1; 77*39571Sbostic if (term_chk(mytty, &msgsok, &atime, 1)) 786202Sroot exit(1); 79*39571Sbostic if (!msgsok) { 80*39571Sbostic (void)fprintf(stderr, 81*39571Sbostic "write: you have write permission turned off.\n"); 82*39571Sbostic exit(1); 831881Serics } 84*39571Sbostic 85*39571Sbostic uid = getuid(); 86*39571Sbostic 87*39571Sbostic /* check args */ 88*39571Sbostic switch (argc) { 89*39571Sbostic case 2: 90*39571Sbostic search_utmp(argv[1], tty, mytty); 91*39571Sbostic do_write(tty, mytty); 92*39571Sbostic break; 93*39571Sbostic case 3: 94*39571Sbostic if (!strncmp(argv[2], "/dev/", 5)) 95*39571Sbostic argv[2] += 5; 96*39571Sbostic if (utmp_chk(argv[1], argv[2])) { 97*39571Sbostic (void)fprintf(stderr, 98*39571Sbostic "write: %s is not logged in on %s.\n", 99*39571Sbostic argv[1], argv[2]); 10026068Skarels exit(1); 1011169Sbill } 102*39571Sbostic if (term_chk(argv[2], &msgsok, &atime, 1)) 103*39571Sbostic exit(1); 104*39571Sbostic if (uid && !msgsok) { 105*39571Sbostic (void)fprintf(stderr, 106*39571Sbostic "write: %s has messages disabled on %s\n", 107*39571Sbostic argv[1], argv[2]); 108*39571Sbostic exit(1); 1091169Sbill } 110*39571Sbostic do_write(argv[2], mytty); 111*39571Sbostic break; 112*39571Sbostic default: 113*39571Sbostic (void)fprintf(stderr, "usage: write user [tty]\n"); 1141169Sbill exit(1); 1151169Sbill } 116*39571Sbostic done(); 117*39571Sbostic /* NOTREACHED */ 118*39571Sbostic } 119*39571Sbostic 120*39571Sbostic /* 121*39571Sbostic * utmp_chk - checks that the given user is actually logged in on 122*39571Sbostic * the given tty 123*39571Sbostic */ 124*39571Sbostic utmp_chk(user, tty) 125*39571Sbostic char *user, *tty; 126*39571Sbostic { 127*39571Sbostic struct utmp u; 128*39571Sbostic int ufd; 129*39571Sbostic 130*39571Sbostic if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 131*39571Sbostic return(0); /* ignore error, shouldn't happen anyway */ 132*39571Sbostic 133*39571Sbostic while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 134*39571Sbostic if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && 135*39571Sbostic strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { 136*39571Sbostic (void)close(ufd); 137*39571Sbostic return(0); 1381169Sbill } 13922608Sserge 140*39571Sbostic (void)close(ufd); 141*39571Sbostic return(1); 142*39571Sbostic } 14322608Sserge 144*39571Sbostic /* 145*39571Sbostic * search_utmp - search utmp for the "best" terminal to write to 146*39571Sbostic * 147*39571Sbostic * Ignores terminals with messages disabled, and of the rest, returns 148*39571Sbostic * the one with the most recent access time. Returns as value the number 149*39571Sbostic * of the user's terminals with messages enabled, or -1 if the user is 150*39571Sbostic * not logged in at all. 151*39571Sbostic * 152*39571Sbostic * Special case for writing to yourself - ignore the terminal you're 153*39571Sbostic * writing from, unless that's the only terminal with messages enabled. 154*39571Sbostic */ 155*39571Sbostic search_utmp(user, tty, mytty) 156*39571Sbostic char *user, *tty, *mytty; 157*39571Sbostic { 158*39571Sbostic struct utmp u; 159*39571Sbostic time_t bestatime, atime; 160*39571Sbostic int ufd, nloggedttys, nttys, msgsok, user_is_me; 161*39571Sbostic char atty[UT_LINESIZE + 1]; 16222608Sserge 163*39571Sbostic if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) { 164*39571Sbostic perror("utmp"); 165*39571Sbostic exit(1); 166*39571Sbostic } 16722608Sserge 168*39571Sbostic nloggedttys = nttys = 0; 169*39571Sbostic bestatime = 0; 170*39571Sbostic user_is_me = 0; 171*39571Sbostic while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 172*39571Sbostic if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { 173*39571Sbostic ++nloggedttys; 174*39571Sbostic STRCPY(atty, u.ut_line); 175*39571Sbostic if (term_chk(atty, &msgsok, &atime, 0)) 176*39571Sbostic continue; /* bad term? skip */ 177*39571Sbostic if (uid && !msgsok) 178*39571Sbostic continue; /* skip ttys with msgs off */ 179*39571Sbostic if (strcmp(atty, mytty) == 0) { 180*39571Sbostic user_is_me = 1; 181*39571Sbostic continue; /* don't write to yourself */ 18222608Sserge } 183*39571Sbostic ++nttys; 184*39571Sbostic if (atime > bestatime) { 185*39571Sbostic bestatime = atime; 186*39571Sbostic (void)strcpy(tty, atty); 187*39571Sbostic } 1887401Skre } 189*39571Sbostic 190*39571Sbostic (void)close(ufd); 191*39571Sbostic if (nloggedttys == 0) { 192*39571Sbostic (void)fprintf(stderr, "write: %s is not logged in\n", user); 193*39571Sbostic exit(1); 1941169Sbill } 195*39571Sbostic if (nttys == 0) { 196*39571Sbostic if (user_is_me) { /* ok, so write to yourself! */ 197*39571Sbostic (void)strcpy(tty, mytty); 198*39571Sbostic return; 199*39571Sbostic } 200*39571Sbostic (void)fprintf(stderr, 201*39571Sbostic "write: %s has messages disabled\n", user); 202*39571Sbostic exit(1); 203*39571Sbostic } else if (nttys > 1) { 204*39571Sbostic (void)fprintf(stderr, 205*39571Sbostic "write: %s is logged in more than once; writing to %s\n", 206*39571Sbostic user, tty); 207*39571Sbostic } 2081169Sbill } 2091169Sbill 210*39571Sbostic /* 211*39571Sbostic * term_chk - check that a terminal exists, and get the message bit 212*39571Sbostic * and the access time 213*39571Sbostic */ 214*39571Sbostic term_chk(tty, msgsokP, atimeP, showerror) 215*39571Sbostic char *tty; 216*39571Sbostic int *msgsokP, showerror; 217*39571Sbostic time_t *atimeP; 2181169Sbill { 219*39571Sbostic struct stat s; 220*39571Sbostic char path[MAXPATHLEN]; 2211169Sbill 222*39571Sbostic (void)sprintf(path, "/dev/%s", tty); 223*39571Sbostic if (stat(path, &s) < 0) { 224*39571Sbostic if (showerror) 225*39571Sbostic (void)fprintf(stderr, "write: %s: %s\n", 226*39571Sbostic path, strerror(errno)); 227*39571Sbostic return(1); 228*39571Sbostic } 229*39571Sbostic *msgsokP = (s.st_mode & (S_IWRITE >> 3)) != 0; /* group write bit */ 230*39571Sbostic *atimeP = s.st_atime; 231*39571Sbostic return(0); 2321169Sbill } 2331169Sbill 234*39571Sbostic /* 235*39571Sbostic * do_write - actually make the connection 236*39571Sbostic */ 237*39571Sbostic do_write(tty, mytty) 238*39571Sbostic char *tty, *mytty; 2391169Sbill { 240*39571Sbostic register char *login, *nows; 241*39571Sbostic register struct passwd *pwd; 242*39571Sbostic time_t now, time(); 243*39571Sbostic char path[MAXPATHLEN], host[MAXHOSTNAMELEN], line[512]; 244*39571Sbostic void done(); 2451169Sbill 246*39571Sbostic (void)sprintf(path, "/dev/%s", tty); 247*39571Sbostic if ((freopen(path, "w", stdout)) == NULL) { 248*39571Sbostic (void)fprintf(stderr, 249*39571Sbostic "write: %s: %s\n", path, strerror(errno)); 250*39571Sbostic exit(1); 251*39571Sbostic } 252*39571Sbostic 253*39571Sbostic /* catch ^C. */ 254*39571Sbostic (void)signal(SIGINT, done); 255*39571Sbostic 256*39571Sbostic /* print greeting. */ 257*39571Sbostic if ((login = getlogin()) == NULL) 258*39571Sbostic if (pwd = getpwuid(getuid())) 259*39571Sbostic login = pwd->pw_name; 260*39571Sbostic else 261*39571Sbostic login = "???"; 262*39571Sbostic if (gethostname(host, sizeof(host)) < 0) 263*39571Sbostic (void)strcpy(host, "???"); 264*39571Sbostic now = time((time_t *)NULL); 265*39571Sbostic nows = ctime(&now); 266*39571Sbostic nows[16] = '\0'; 267*39571Sbostic (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", 268*39571Sbostic login, host, mytty, nows + 11); 269*39571Sbostic 270*39571Sbostic while (fgets(line, sizeof(line), stdin) != NULL) 271*39571Sbostic massage_fputs(line); 272*39571Sbostic } 273*39571Sbostic 274*39571Sbostic /* 275*39571Sbostic * done - cleanup and exit 276*39571Sbostic */ 277*39571Sbostic void 278*39571Sbostic done() 279*39571Sbostic { 280*39571Sbostic (void)printf("EOF\r\n"); 2811169Sbill exit(0); 2821169Sbill } 2831169Sbill 284*39571Sbostic /* 285*39571Sbostic * massage_fputs - like fputs(), but makes control characters visible and 286*39571Sbostic * turns \n into \r\n 287*39571Sbostic */ 288*39571Sbostic massage_fputs(s) 289*39571Sbostic register char *s; 2901169Sbill { 291*39571Sbostic register char c; 2921169Sbill 293*39571Sbostic #define PUTC(c) if (putchar(c) == EOF) goto err; 294*39571Sbostic 295*39571Sbostic for (; *s != '\0'; ++s) { 296*39571Sbostic c = toascii(*s); 297*39571Sbostic if (c == '\n') { 298*39571Sbostic PUTC('\r'); 299*39571Sbostic PUTC('\n'); 300*39571Sbostic } else if (!isprint(c) && !isspace(c) && c != '\007') { 301*39571Sbostic PUTC('^'); 302*39571Sbostic PUTC(c^0x40); /* DEL to ?, others to alpha */ 303*39571Sbostic } else 304*39571Sbostic PUTC(c); 3051169Sbill } 306*39571Sbostic return; 3071169Sbill 308*39571Sbostic err: (void)fprintf(stderr, "write: %s\n", strerror(errno)); 309*39571Sbostic exit(1); 310*39571Sbostic #undef PUTC 3111169Sbill } 312