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