Upgrade to version 1.05
[unix-history] / gnu / libexec / uucp / common_sources / tli.c
CommitLineData
41c799d4
C
1/* tli.c
2 Code to handle TLI connections.
3
3469b437 4 Copyright (C) 1992, 1993, 1994 Ian Lance Taylor
41c799d4
C
5
6 This file is part of the Taylor UUCP package.
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21
22 The author of the program may be contacted at ian@airs.com or
3469b437 23 c/o Cygnus Support, Building 200, 1 Kendall Square, Cambridge, MA 02139.
41c799d4
C
24 */
25
26#include "uucp.h"
27
28#if USE_RCS_ID
3469b437 29const char tli_rcsid[] = "$Id: tli.c,v 1.15 1994/01/30 20:59:40 ian Rel $";
41c799d4
C
30#endif
31
32#if HAVE_TLI
33
34#include "sysdep.h"
35#include "uudefs.h"
36#include "uuconf.h"
37#include "conn.h"
38#include "system.h"
39
40#include <errno.h>
41
42#if HAVE_SYS_IOCTL_H
43#include <sys/ioctl.h>
44#endif
45
46#if HAVE_TIUSER_H
47#include <tiuser.h>
48#else
49#if HAVE_XTI_H
50#include <xti.h>
51#else
52#if HAVE_SYS_TLI_H
53#include <sys/tli.h>
54#endif
55#endif
56#endif
57
58#if HAVE_STROPTS_H
59#include <stropts.h>
60#endif
61
62#if HAVE_FCNTL_H
63#include <fcntl.h>
64#else
65#if HAVE_SYS_FILE_H
66#include <sys/file.h>
67#endif
68#endif
69
70#ifndef O_RDONLY
71#define O_RDONLY 0
72#define O_WRONLY 1
73#define O_RDWR 2
74#endif
75
76#ifndef FD_CLOEXEC
77#define FD_CLOEXEC 1
78#endif
79
80/* The arguments to t_alloca have two different names. I want the
81 SVID ones, not the XPG3 ones. */
82#ifndef T_BIND
83#define T_BIND T_BIND_STR
84#endif
85#ifndef T_CALL
86#define T_CALL T_CALL_STR
87#endif
88
89/* Hopefully these externs will not cause any trouble. This is how
90 they are shown in the SVID. */
91extern int t_errno;
92extern char *t_errlist[];
93extern int t_nerr;
94
3469b437 95#ifndef HAVE_TIUSER_H
41c799d4
C
96#ifndef t_alloc
97extern pointer t_alloc ();
98#endif
3469b437 99#endif
41c799d4
C
100\f
101/* This code handles TLI connections. It's Unix specific. It's
102 largely based on code from Unix Network Programming, by W. Richard
103 Stevens. */
104\f
105/* Local functions. */
106static const char *ztlierror P((void));
107static void utli_free P((struct sconnection *qconn));
108static boolean ftli_push P((struct sconnection *qconn));
109static boolean ftli_open P((struct sconnection *qconn, long ibaud,
110 boolean fwait));
111static boolean ftli_close P((struct sconnection *qconn,
112 pointer puuconf,
113 struct uuconf_dialer *qdialer,
114 boolean fsuccess));
41c799d4
C
115static boolean ftli_dial P((struct sconnection *qconn, pointer puuconf,
116 const struct uuconf_system *qsys,
117 const char *zphone,
118 struct uuconf_dialer *qdialer,
119 enum tdialerfound *ptdialer));
120\f
121/* The command table for a TLI connection. */
122static const struct sconncmds stlicmds =
123{
124 utli_free,
125 NULL, /* pflock */
126 NULL, /* pfunlock */
127 ftli_open,
128 ftli_close,
41c799d4
C
129 ftli_dial,
130 fsysdep_conn_read,
131 fsysdep_conn_write,
132 fsysdep_conn_io,
133 NULL, /* pfbreak */
134 NULL, /* pfset */
135 NULL, /* pfcarrier */
136 fsysdep_conn_chat,
137 NULL /* pibaud */
138};
139\f
140/* Get a TLI error string. */
141
142static const char *
143ztlierror ()
144{
145 if (t_errno == TSYSERR)
146 return strerror (errno);
147 if (t_errno < 0 || t_errno >= t_nerr)
148 return "Unknown TLI error";
149 return t_errlist[t_errno];
150}
151\f
3469b437
AC
152/* Initialize a TLI connection. This may be called with qconn->qport
153 NULL, when opening standard input as a TLI connection. */
41c799d4
C
154
155boolean
156fsysdep_tli_init (qconn)
157 struct sconnection *qconn;
158{
159 struct ssysdep_conn *q;
160
161 q = (struct ssysdep_conn *) xmalloc (sizeof (struct ssysdep_conn));
162 q->o = -1;
3469b437
AC
163 q->ord = -1;
164 q->owr = -1;
41c799d4
C
165 q->zdevice = NULL;
166 q->iflags = -1;
3469b437 167 q->iwr_flags = -1;
41c799d4
C
168 q->fterminal = FALSE;
169 q->ftli = TRUE;
170 q->ibaud = 0;
171
172 qconn->psysdep = (pointer) q;
173 qconn->qcmds = &stlicmds;
174 return TRUE;
175}
176
177/* Free a TLI connection. */
178
179static void
180utli_free (qconn)
181 struct sconnection *qconn;
182{
183 xfree (qconn->psysdep);
184}
185\f
186/* Push all desired modules onto a TLI stream. If the user requests a
187 STREAMS connection without giving a list of modules, we just push
188 tirdwr. If the I_PUSH ioctl is not defined on this system, we just
189 ignore any list of modules. */
190
191static boolean
192ftli_push (qconn)
193 struct sconnection *qconn;
194{
195#ifdef I_PUSH
196
197 struct ssysdep_conn *qsysdep;
198
199 qsysdep = (struct ssysdep_conn *) qconn->psysdep;
200
201 if (qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush != NULL)
202 {
203 char **pz;
204
205 for (pz = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzpush;
206 *pz != NULL;
207 pz++)
208 {
209 if (ioctl (qsysdep->o, I_PUSH, *pz) < 0)
210 {
211 ulog (LOG_ERROR, "ioctl (I_PUSH, %s): %s", *pz,
212 strerror (errno));
213 return FALSE;
214 }
215 }
216 }
217 else if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream)
218 {
219 if (ioctl (qsysdep->o, I_PUSH, "tirdwr") < 0)
220 {
221 ulog (LOG_ERROR, "ioctl (I_PUSH, tirdwr): %s",
222 strerror (errno));
223 return FALSE;
224 }
225 }
226
227 /* If we have just put the connection into stream mode, we must turn
228 off the TLI flag to avoid using TLI calls on it. */
229 if (qconn->qport->uuconf_u.uuconf_stli.uuconf_fstream)
230 qsysdep->ftli = FALSE;
231
232#endif /* defined (I_PUSH) */
233
234 return TRUE;
235}
236\f
237/* Open a TLI connection. If the fwait argument is TRUE, we are
238 running as a server. Otherwise we are just trying to reach another
239 system. */
240
241static boolean
242ftli_open (qconn, ibaud, fwait)
243 struct sconnection *qconn;
244 long ibaud;
245 boolean fwait;
246{
247 struct ssysdep_conn *qsysdep;
248 const char *zdevice;
249 char *zfreedev;
250 const char *zservaddr;
251 char *zfreeaddr;
252 struct t_bind *qtbind;
253 struct t_call *qtcall;
254
255 /* Unlike most other device types, we don't bother to call
256 ulog_device here, because fconn_open calls it with the name of
257 the port anyhow. */
258
259 qsysdep = (struct ssysdep_conn *) qconn->psysdep;
260
261 zdevice = qconn->qport->uuconf_u.uuconf_stli.uuconf_zdevice;
262 if (zdevice == NULL)
263 zdevice = qconn->qport->uuconf_zname;
264
265 zfreedev = NULL;
266 if (*zdevice != '/')
267 {
268 zfreedev = zbufalc (sizeof "/dev/" + strlen (zdevice));
269 sprintf (zfreedev, "/dev/%s", zdevice);
270 zdevice = zfreedev;
271 }
272
273 qsysdep->o = t_open (zdevice, O_RDWR, (struct t_info *) NULL);
274 if (qsysdep->o < 0)
275 {
276 ulog (LOG_ERROR, "t_open (%s): %s", zdevice, ztlierror ());
277 ubuffree (zfreedev);
278 return FALSE;
279 }
280
281 if (fcntl (qsysdep->o, F_SETFD,
282 fcntl (qsysdep->o, F_GETFD, 0) | FD_CLOEXEC) < 0)
283 {
284 ulog (LOG_ERROR, "fcntl (FD_CLOEXEC): %s", strerror (errno));
285 ubuffree (zfreedev);
286 (void) t_close (qsysdep->o);
287 qsysdep->o = -1;
288 return FALSE;
289 }
290
291 qsysdep->iflags = fcntl (qsysdep->o, F_GETFL, 0);
292 if (qsysdep->iflags < 0)
293 {
294 ulog (LOG_ERROR, "fcntl: %s", strerror (errno));
295 ubuffree (zfreedev);
296 (void) t_close (qsysdep->o);
297 qsysdep->o = -1;
298 return FALSE;
299 }
300
3469b437
AC
301 /* We save our process ID in the qconn structure. This is checked
302 in ftli_close. */
303 qsysdep->ipid = getpid ();
304
41c799d4
C
305 /* If we aren't waiting for a connection, we can bind to any local
306 address, and then we're finished. */
307 if (! fwait)
308 {
309 ubuffree (zfreedev);
310 if (t_bind (qsysdep->o, (struct t_bind *) NULL,
311 (struct t_bind *) NULL) < 0)
312 {
313 ulog (LOG_ERROR, "t_bind: %s", ztlierror ());
314 (void) t_close (qsysdep->o);
315 qsysdep->o = -1;
316 return FALSE;
317 }
318 return TRUE;
319 }
320
321 /* Run as a server and wait for a new connection. The code in
322 uucico.c has already detached us from our controlling terminal.
323 From this point on if the server gets an error we exit; we only
324 return if we have received a connection. It would be more robust
325 to respawn the server if it fails; someday. */
326 qtbind = (struct t_bind *) t_alloc (qsysdep->o, T_BIND, T_ALL);
327 if (qtbind == NULL)
328 ulog (LOG_FATAL, "t_alloc (T_BIND): %s", ztlierror ());
329
330 zservaddr = qconn->qport->uuconf_u.uuconf_stli.uuconf_zservaddr;
331 if (zservaddr == NULL)
332 ulog (LOG_FATAL, "Can't run as TLI server; no server address");
333
334 zfreeaddr = zbufcpy (zservaddr);
335 qtbind->addr.len = cescape (zfreeaddr);
336 if (qtbind->addr.len > qtbind->addr.maxlen)
337 ulog (LOG_FATAL, "%s: TLI server address too long (max %d)",
338 zservaddr, qtbind->addr.maxlen);
339 memcpy (qtbind->addr.buf, zfreeaddr, qtbind->addr.len);
340 ubuffree (zfreeaddr);
341
342 qtbind->qlen = 5;
343
344 if (t_bind (qsysdep->o, qtbind, (struct t_bind *) NULL) < 0)
345 ulog (LOG_FATAL, "t_bind (%s): %s", zservaddr, ztlierror ());
346
347 (void) t_free ((pointer) qtbind, T_BIND);
348
349 qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ALL);
350 if (qtcall == NULL)
351 ulog (LOG_FATAL, "t_alloc (T_CALL): %s", ztlierror ());
352
353 while (! FGOT_SIGNAL ())
354 {
355 int onew;
356 pid_t ipid;
357
358 DEBUG_MESSAGE0 (DEBUG_PORT,
359 "ftli_open: Waiting for connections");
360
361 if (t_listen (qsysdep->o, qtcall) < 0)
362 ulog (LOG_FATAL, "t_listen: %s", ztlierror ());
363
364 onew = t_open (zdevice, O_RDWR, (struct t_info *) NULL);
365 if (onew < 0)
366 ulog (LOG_FATAL, "t_open (%s): %s", zdevice, ztlierror ());
367
368 if (fcntl (onew, F_SETFD,
369 fcntl (onew, F_GETFD, 0) | FD_CLOEXEC) < 0)
370 ulog (LOG_FATAL, "fcntl (FD_CLOEXEC): %s", strerror (errno));
371
372 if (t_bind (onew, (struct t_bind *) NULL, (struct t_bind *) NULL) < 0)
373 ulog (LOG_FATAL, "t_bind: %s", ztlierror ());
374
375 if (t_accept (qsysdep->o, onew, qtcall) < 0)
376 {
377 /* We may have received a disconnect. */
378 if (t_errno != TLOOK)
379 ulog (LOG_FATAL, "t_accept: %s", ztlierror ());
380 if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0)
381 ulog (LOG_FATAL, "t_rcvdis: %s", ztlierror ());
382 (void) t_close (onew);
383 continue;
384 }
385
386 DEBUG_MESSAGE0 (DEBUG_PORT,
387 "ftli_open: Got connection; forking");
388
389 ipid = ixsfork ();
390 if (ipid < 0)
391 ulog (LOG_FATAL, "fork: %s", strerror (errno));
392 if (ipid == 0)
393 {
394 ulog_close ();
395
396 (void) t_close (qsysdep->o);
397 qsysdep->o = onew;
398
399 /* Push any desired modules. */
400 if (! ftli_push (qconn))
401 _exit (EXIT_FAILURE);
402
403 /* Now we fork and let our parent die, so that we become
404 a child of init. This lets the main server code wait
405 for its child and then continue without accumulating
406 zombie children. */
407 ipid = ixsfork ();
408 if (ipid < 0)
409 {
410 ulog (LOG_ERROR, "fork: %s", strerror (errno));
411 _exit (EXIT_FAILURE);
412 }
413
414 if (ipid != 0)
415 _exit (EXIT_SUCCESS);
416
417 ulog_id (getpid ());
418
419 return TRUE;
420 }
421
422 (void) t_close (onew);
423
424 /* Now wait for the child. */
425 (void) ixswait ((unsigned long) ipid, (const char *) NULL);
426 }
427
428 /* We got a signal. */
429 usysdep_exit (FALSE);
430
431 /* Avoid compiler warnings. */
432 return FALSE;
433}
434\f
435/* Close the port. */
436
437/*ARGSUSED*/
438static boolean
439ftli_close (qconn, puuconf, qdialer, fsuccess)
440 struct sconnection *qconn;
441 pointer puuconf;
442 struct uuconf_dialer *qdialer;
443 boolean fsuccess;
444{
445 struct ssysdep_conn *qsysdep;
446 boolean fret;
447
448 qsysdep = (struct ssysdep_conn *) qconn->psysdep;
449
450 fret = TRUE;
451 if (qsysdep->o >= 0)
452 {
453 if (qsysdep->ftli)
454 {
455 if (t_close (qsysdep->o) < 0)
456 {
457 ulog (LOG_ERROR, "t_close: %s", ztlierror ());
458 fret = FALSE;
459 }
460 }
461 else
462 {
463 if (close (qsysdep->o) < 0)
464 {
465 ulog (LOG_ERROR, "close: %s", strerror (errno));
466 fret = FALSE;
467 }
468 }
469
470 qsysdep->o = -1;
471 }
472
3469b437
AC
473 /* If the current pid is not the one we used to open the port, then
474 we must have forked up above and we are now the child. In this
475 case, we are being called from within the fendless loop in
476 uucico.c. We return FALSE to force the loop to end and the child
477 to exit. This should be handled in a cleaner fashion. */
478 if (qsysdep->ipid != getpid ())
479 fret = FALSE;
41c799d4 480
3469b437 481 return fret;
41c799d4
C
482}
483\f
484/* Dial out on a TLI port, so to speak: connect to a remote computer. */
485
486/*ARGSUSED*/
487static boolean
488ftli_dial (qconn, puuconf, qsys, zphone, qdialer, ptdialerfound)
489 struct sconnection *qconn;
490 pointer puuconf;
491 const struct uuconf_system *qsys;
492 const char *zphone;
493 struct uuconf_dialer *qdialer;
494 enum tdialerfound *ptdialerfound;
495{
496 struct ssysdep_conn *qsysdep;
497 char **pzdialer;
498 const char *zaddr;
499 struct t_call *qtcall;
500 char *zescape;
501
502 qsysdep = (struct ssysdep_conn *) qconn->psysdep;
503
504 *ptdialerfound = DIALERFOUND_FALSE;
505
506 pzdialer = qconn->qport->uuconf_u.uuconf_stli.uuconf_pzdialer;
507 if (*pzdialer == NULL)
508 pzdialer = NULL;
509
510 /* If the first dialer is "TLI" or "TLIS", we use the first token
511 (pzdialer[1]) as the address to connect to. */
512 zaddr = zphone;
513 if (pzdialer != NULL
514 && (strcmp (pzdialer[0], "TLI") == 0
515 || strcmp (pzdialer[0], "TLIS") == 0))
516 {
517 if (pzdialer[1] == NULL)
518 ++pzdialer;
519 else
520 {
521 if (strcmp (pzdialer[1], "\\D") != 0
522 && strcmp (pzdialer[1], "\\T") != 0)
523 zaddr = pzdialer[1];
524 pzdialer += 2;
525 }
526 }
527
528 if (zaddr == NULL)
529 {
530 ulog (LOG_ERROR, "No address for TLI connection");
531 return FALSE;
532 }
533
534 qtcall = (struct t_call *) t_alloc (qsysdep->o, T_CALL, T_ADDR);
535 if (qtcall == NULL)
536 {
537 ulog (LOG_ERROR, "t_alloc (T_CALL): %s", ztlierror ());
538 return FALSE;
539 }
540
541 zescape = zbufcpy (zaddr);
542 qtcall->addr.len = cescape (zescape);
543 if (qtcall->addr.len > qtcall->addr.maxlen)
544 {
545 ulog (LOG_ERROR, "%s: TLI address too long (max %d)", zaddr,
546 qtcall->addr.maxlen);
547 ubuffree (zescape);
548 return FALSE;
549 }
550 memcpy (qtcall->addr.buf, zescape, qtcall->addr.len);
551 ubuffree (zescape);
552
553 if (t_connect (qsysdep->o, qtcall, (struct t_call *) NULL) < 0)
554 {
555 if (t_errno != TLOOK)
556 ulog (LOG_ERROR, "t_connect: %s", ztlierror ());
557 else
558 {
559 if (t_rcvdis (qsysdep->o, (struct t_discon *) NULL) < 0)
560 ulog (LOG_ERROR, "t_rcvdis: %s", ztlierror ());
561 else
562 ulog (LOG_ERROR, "Connection refused");
563 }
564 return FALSE;
565 }
566
567 /* We've connected to the remote. Push any desired modules. */
568 if (! ftli_push (qconn))
569 return FALSE;
570
3469b437
AC
571 /* Handle the rest of the dialer sequence. */
572 if (pzdialer != NULL && *pzdialer != NULL)
41c799d4 573 {
3469b437
AC
574 if (! fconn_dial_sequence (qconn, puuconf, pzdialer, qsys, zphone,
575 qdialer, ptdialerfound))
576 return FALSE;
41c799d4
C
577 }
578
579 return TRUE;
580}
581
582#endif /* HAVE_TLI */