1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 * 22 * Copyright 2002 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T 28 * All Rights Reserved. 29 */ 30 31 /* 32 * University Copyright- Copyright (c) 1982, 1986, 1988 33 * The Regents of the University of California. 34 * All Rights Reserved. 35 * 36 * University Acknowledgment- Portions of this document are derived from 37 * software developed by the University of California, Berkeley, and its 38 * contributors. 39 */ 40 41 #pragma ident "%Z%%M% %I% %E% SMI" 42 43 44 #include <sys/types.h> 45 #include <sys/stat.h> 46 #include <sys/ioctl.h> 47 #include <sys/fcntl.h> 48 #include <sys/time.h> 49 #include <time.h> 50 #include <stdio.h> 51 #include <sys/wait.h> 52 #include <errno.h> 53 #include <fcntl.h> 54 #include <pwd.h> 55 #include <unistd.h> 56 #include <ctype.h> 57 #include <string.h> 58 #include "talkd_impl.h" 59 60 static int nofork = 0; /* to be set from the debugger */ 61 62 static int announce_proc(CTL_MSG *request, char *remote_machine); 63 static void print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine); 64 65 /* 66 * Because the tty driver insists on attaching a terminal-less 67 * process to any terminal that it writes on, we must fork a child 68 * to protect ourselves. 69 */ 70 71 int 72 announce(CTL_MSG *request, char *remote_machine) 73 { 74 pid_t pid, val; 75 int status; 76 77 if (nofork) { 78 return (announce_proc(request, remote_machine)); 79 } 80 81 if (pid = fork()) { 82 83 /* we are the parent, so wait for the child */ 84 if (pid == (pid_t)-1) { 85 /* the fork failed */ 86 return (FAILED); 87 } 88 89 do { 90 val = wait(&status); 91 if (val == (pid_t)-1) { 92 if (errno == EINTR) { 93 continue; 94 } else { 95 /* shouldn't happen */ 96 print_error("wait"); 97 return (FAILED); 98 } 99 } 100 } while (val != pid); 101 102 if ((status & 0377) > 0) { 103 /* we were killed by some signal */ 104 return (FAILED); 105 } 106 107 /* Get the second byte, this is the exit/return code */ 108 return ((status>>8)&0377); 109 } else { 110 /* we are the child, go and do it */ 111 _exit(announce_proc(request, remote_machine)); 112 } 113 /* NOTREACHED */ 114 } 115 116 117 /* 118 * See if the user is accepting messages. If so, announce that 119 * a talk is requested. 120 */ 121 static int 122 announce_proc(CTL_MSG *request, char *remote_machine) 123 { 124 #define TTY_BUFSZ 32 125 char full_tty[TTY_BUFSZ]; 126 FILE *tf; 127 struct stat stbuf; 128 int fd; 129 struct passwd *p; 130 131 (void) snprintf(full_tty, TTY_BUFSZ, "/dev/%s", request->r_tty); 132 p = getpwnam(request->r_name); 133 134 if (p == 0 || access(full_tty, 0) != 0) { 135 return (FAILED); 136 } 137 138 /* fopen uses O_CREAT|O_TRUNC, we don't want that */ 139 if ((fd = open(full_tty, O_WRONLY|O_NONBLOCK)) == -1) { 140 return (PERMISSION_DENIED); 141 } 142 /* must be tty */ 143 if (!isatty(fd)) { 144 (void) close(fd); 145 return (PERMISSION_DENIED); 146 } 147 148 /* 149 * open gratuitously attaches the talkd to any tty it opens, so 150 * disconnect us from the tty before we catch a signal 151 */ 152 (void) setsid(); 153 154 if (fstat(fd, &stbuf) < 0 || stbuf.st_uid != p->pw_uid) { 155 (void) close(fd); 156 return (PERMISSION_DENIED); 157 } 158 159 if ((stbuf.st_mode&020) == 0) { 160 (void) close(fd); 161 return (PERMISSION_DENIED); 162 } 163 164 if ((tf = fdopen(fd, "w")) == NULL) { 165 (void) close(fd); 166 return (PERMISSION_DENIED); 167 } 168 169 print_mesg(tf, request, remote_machine); 170 (void) fclose(tf); 171 return (SUCCESS); 172 } 173 174 #define max(a, b) ((a) > (b) ? (a) : (b)) 175 #define N_LINES 5 176 #define N_CHARS 300 177 178 /* 179 * Build a block of characters containing the message. 180 * It is sent blank filled and in a single block to 181 * try to keep the message in one piece if the recipient 182 * is in vi at the time. 183 */ 184 static void 185 print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine) 186 { 187 struct timeval clock; 188 struct tm *localclock; 189 char line_buf[N_LINES][N_CHARS]; 190 int sizes[N_LINES]; 191 char *bptr, *lptr; 192 int i, j, max_size; 193 194 /* 195 * [3 wakeup chars + (lines * max chars/line) + 196 * (lines * strlen("\r\n")) + 1(NUL)]. 197 */ 198 char big_buf[3 + (N_LINES * (N_CHARS - 1)) + (N_LINES * 2) + 1]; 199 /* 200 * ( (length of (request->l_name) - 1(NUL)) * 201 * (strlen("M-") + 1('^') + 1(printable char)) ) + 1(NUL). 202 */ 203 char l_username[((NAME_SIZE - 1) * 4) + 1]; 204 int len, k; 205 206 i = 0; 207 max_size = 0; 208 209 (void) gettimeofday(&clock, NULL); 210 localclock = localtime(&clock.tv_sec); 211 212 (void) sprintf(line_buf[i], " "); 213 214 sizes[i] = strlen(line_buf[i]); 215 max_size = max(max_size, sizes[i]); 216 i++; 217 218 (void) snprintf(line_buf[i], N_CHARS, 219 "Message from Talk_Daemon@%s at %d:%02d ...", hostname, 220 localclock->tm_hour, localclock->tm_min); 221 222 sizes[i] = strlen(line_buf[i]); 223 max_size = max(max_size, sizes[i]); 224 i++; 225 226 len = (strlen(request->l_name) > NAME_SIZE - 1) ? (NAME_SIZE - 1) : 227 strlen(request->l_name); 228 for (j = 0, k = 0; j < len; j++) { 229 if (!isprint((unsigned char)request->l_name[j])) { 230 char c; 231 if (!isascii((unsigned char)request->l_name[j])) { 232 l_username[k++] = 'M'; 233 l_username[k++] = '-'; 234 c = toascii(request->l_name[j]); 235 } 236 if (iscntrl((unsigned char)request->l_name[j])) { 237 l_username[k++] = '^'; 238 /* add decimal 64 to the control character */ 239 c = request->l_name[j] + 0100; 240 } 241 l_username[k++] = c; 242 } else { 243 l_username[k++] = request->l_name[j]; 244 } 245 } 246 l_username[k] = '\0'; 247 248 (void) snprintf(line_buf[i], N_CHARS, 249 "talk: connection requested by %s@%s.", l_username, remote_machine); 250 251 sizes[i] = strlen(line_buf[i]); 252 max_size = max(max_size, sizes[i]); 253 i++; 254 255 (void) snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s", 256 l_username, remote_machine); 257 258 sizes[i] = strlen(line_buf[i]); 259 max_size = max(max_size, sizes[i]); 260 i++; 261 262 (void) sprintf(line_buf[i], " "); 263 264 sizes[i] = strlen(line_buf[i]); 265 max_size = max(max_size, sizes[i]); 266 i++; 267 268 bptr = big_buf; 269 *(bptr++) = '\a'; /* send something to wake them up */ 270 *(bptr++) = '\r'; /* add a \r in case of raw mode */ 271 *(bptr++) = '\n'; 272 for (i = 0; i < N_LINES; i++) { 273 /* copy the line into the big buffer */ 274 lptr = line_buf[i]; 275 while (*lptr != '\0') { 276 *(bptr++) = *(lptr++); 277 } 278 279 /* pad out the rest of the lines with blanks */ 280 for (j = sizes[i]; j < max_size; j++) { 281 *(bptr++) = ' '; 282 } 283 284 *(bptr++) = '\r'; /* add a \r in case of raw mode */ 285 *(bptr++) = '\n'; 286 } 287 *bptr = '\0'; 288 289 (void) fputs(big_buf, tf); 290 (void) fflush(tf); 291 (void) setsid(); 292 } 293