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
announce(CTL_MSG * request,char * remote_machine)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
announce_proc(CTL_MSG * request,char * remote_machine)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
print_mesg(FILE * tf,CTL_MSG * request,char * remote_machine)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