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 #include <sys/types.h> 42 #include <sys/stat.h> 43 #include <sys/ioctl.h> 44 #include <sys/fcntl.h> 45 #include <sys/time.h> 46 #include <time.h> 47 #include <stdio.h> 48 #include <sys/wait.h> 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <pwd.h> 52 #include <unistd.h> 53 #include <ctype.h> 54 #include <string.h> 55 #include "talkd_impl.h" 56 57 static int nofork = 0; /* to be set from the debugger */ 58 59 static int announce_proc(CTL_MSG *request, char *remote_machine); 60 static void print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine); 61 62 /* 63 * Because the tty driver insists on attaching a terminal-less 64 * process to any terminal that it writes on, we must fork a child 65 * to protect ourselves. 66 */ 67 68 int 69 announce(CTL_MSG *request, char *remote_machine) 70 { 71 pid_t pid, val; 72 int status; 73 74 if (nofork) { 75 return (announce_proc(request, remote_machine)); 76 } 77 78 if (pid = fork()) { 79 80 /* we are the parent, so wait for the child */ 81 if (pid == (pid_t)-1) { 82 /* the fork failed */ 83 return (FAILED); 84 } 85 86 do { 87 val = wait(&status); 88 if (val == (pid_t)-1) { 89 if (errno == EINTR) { 90 continue; 91 } else { 92 /* shouldn't happen */ 93 print_error("wait"); 94 return (FAILED); 95 } 96 } 97 } while (val != pid); 98 99 if ((status & 0377) > 0) { 100 /* we were killed by some signal */ 101 return (FAILED); 102 } 103 104 /* Get the second byte, this is the exit/return code */ 105 return ((status>>8)&0377); 106 } else { 107 /* we are the child, go and do it */ 108 _exit(announce_proc(request, remote_machine)); 109 } 110 /* NOTREACHED */ 111 } 112 113 114 /* 115 * See if the user is accepting messages. If so, announce that 116 * a talk is requested. 117 */ 118 static int 119 announce_proc(CTL_MSG *request, char *remote_machine) 120 { 121 #define TTY_BUFSZ 32 122 char full_tty[TTY_BUFSZ]; 123 FILE *tf; 124 struct stat stbuf; 125 int fd; 126 struct passwd *p; 127 128 (void) snprintf(full_tty, TTY_BUFSZ, "/dev/%s", request->r_tty); 129 p = getpwnam(request->r_name); 130 131 if (p == 0 || access(full_tty, 0) != 0) { 132 return (FAILED); 133 } 134 135 /* fopen uses O_CREAT|O_TRUNC, we don't want that */ 136 if ((fd = open(full_tty, O_WRONLY|O_NONBLOCK)) == -1) { 137 return (PERMISSION_DENIED); 138 } 139 /* must be tty */ 140 if (!isatty(fd)) { 141 (void) close(fd); 142 return (PERMISSION_DENIED); 143 } 144 145 /* 146 * open gratuitously attaches the talkd to any tty it opens, so 147 * disconnect us from the tty before we catch a signal 148 */ 149 (void) setsid(); 150 151 if (fstat(fd, &stbuf) < 0 || stbuf.st_uid != p->pw_uid) { 152 (void) close(fd); 153 return (PERMISSION_DENIED); 154 } 155 156 if ((stbuf.st_mode&020) == 0) { 157 (void) close(fd); 158 return (PERMISSION_DENIED); 159 } 160 161 if ((tf = fdopen(fd, "w")) == NULL) { 162 (void) close(fd); 163 return (PERMISSION_DENIED); 164 } 165 166 print_mesg(tf, request, remote_machine); 167 (void) fclose(tf); 168 return (SUCCESS); 169 } 170 171 #define max(a, b) ((a) > (b) ? (a) : (b)) 172 #define N_LINES 5 173 #define N_CHARS 300 174 175 /* 176 * Build a block of characters containing the message. 177 * It is sent blank filled and in a single block to 178 * try to keep the message in one piece if the recipient 179 * is in vi at the time. 180 */ 181 static void 182 print_mesg(FILE *tf, CTL_MSG *request, char *remote_machine) 183 { 184 struct timeval clock; 185 struct tm *localclock; 186 char line_buf[N_LINES][N_CHARS]; 187 int sizes[N_LINES]; 188 char *bptr, *lptr; 189 int i, j, max_size; 190 191 /* 192 * [3 wakeup chars + (lines * max chars/line) + 193 * (lines * strlen("\r\n")) + 1(NUL)]. 194 */ 195 char big_buf[3 + (N_LINES * (N_CHARS - 1)) + (N_LINES * 2) + 1]; 196 /* 197 * ( (length of (request->l_name) - 1(NUL)) * 198 * (strlen("M-") + 1('^') + 1(printable char)) ) + 1(NUL). 199 */ 200 char l_username[((NAME_SIZE - 1) * 4) + 1]; 201 int len, k; 202 203 i = 0; 204 max_size = 0; 205 206 (void) gettimeofday(&clock, NULL); 207 localclock = localtime(&clock.tv_sec); 208 209 (void) sprintf(line_buf[i], " "); 210 211 sizes[i] = strlen(line_buf[i]); 212 max_size = max(max_size, sizes[i]); 213 i++; 214 215 (void) snprintf(line_buf[i], N_CHARS, 216 "Message from Talk_Daemon@%s at %d:%02d ...", hostname, 217 localclock->tm_hour, localclock->tm_min); 218 219 sizes[i] = strlen(line_buf[i]); 220 max_size = max(max_size, sizes[i]); 221 i++; 222 223 len = (strlen(request->l_name) > NAME_SIZE - 1) ? (NAME_SIZE - 1) : 224 strlen(request->l_name); 225 for (j = 0, k = 0; j < len; j++) { 226 if (!isprint((unsigned char)request->l_name[j])) { 227 char c; 228 if (!isascii((unsigned char)request->l_name[j])) { 229 l_username[k++] = 'M'; 230 l_username[k++] = '-'; 231 c = toascii(request->l_name[j]); 232 } 233 if (iscntrl((unsigned char)request->l_name[j])) { 234 l_username[k++] = '^'; 235 /* add decimal 64 to the control character */ 236 c = request->l_name[j] + 0100; 237 } 238 l_username[k++] = c; 239 } else { 240 l_username[k++] = request->l_name[j]; 241 } 242 } 243 l_username[k] = '\0'; 244 245 (void) snprintf(line_buf[i], N_CHARS, 246 "talk: connection requested by %s@%s.", l_username, remote_machine); 247 248 sizes[i] = strlen(line_buf[i]); 249 max_size = max(max_size, sizes[i]); 250 i++; 251 252 (void) snprintf(line_buf[i], N_CHARS, "talk: respond with: talk %s@%s", 253 l_username, remote_machine); 254 255 sizes[i] = strlen(line_buf[i]); 256 max_size = max(max_size, sizes[i]); 257 i++; 258 259 (void) sprintf(line_buf[i], " "); 260 261 sizes[i] = strlen(line_buf[i]); 262 max_size = max(max_size, sizes[i]); 263 i++; 264 265 bptr = big_buf; 266 *(bptr++) = '\a'; /* send something to wake them up */ 267 *(bptr++) = '\r'; /* add a \r in case of raw mode */ 268 *(bptr++) = '\n'; 269 for (i = 0; i < N_LINES; i++) { 270 /* copy the line into the big buffer */ 271 lptr = line_buf[i]; 272 while (*lptr != '\0') { 273 *(bptr++) = *(lptr++); 274 } 275 276 /* pad out the rest of the lines with blanks */ 277 for (j = sizes[i]; j < max_size; j++) { 278 *(bptr++) = ' '; 279 } 280 281 *(bptr++) = '\r'; /* add a \r in case of raw mode */ 282 *(bptr++) = '\n'; 283 } 284 *bptr = '\0'; 285 286 (void) fputs(big_buf, tf); 287 (void) fflush(tf); 288 (void) setsid(); 289 } 290