This is the main UUCP communication program.
Copyright (C) 1991, 1992 Ian Lance Taylor
This file is part of the Taylor UUCP package.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
The author of the program may be contacted at ian@airs.com or
c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
const char uucico_rcsid
[] = "$Id: uucico.c,v 1.1 1993/08/04 19:36:34 jtc Exp $";
#define LONG_MAX 2147483647L
char abProgram
[] = "uucico";
/* Define the known protocols. */
(UUCONF_RELIABLE_ENDTOEND \
| UUCONF_RELIABLE_RELIABLE \
static const struct sprotocol asProtocols
[] =
asTproto_params
, ftstart
, ftshutdown
, ftsendcmd
, ztgetspace
,
ftsenddata
, ftwait
, ftfile
},
asEproto_params
, festart
, feshutdown
, fesendcmd
, zegetspace
,
fesenddata
, fewait
, fefile
},
{ 'i', UUCONF_RELIABLE_EIGHT
, 7,
asIproto_params
, fistart
, fishutdown
, fisendcmd
, zigetspace
,
fisenddata
, fiwait
, NULL
},
{ 'a', UUCONF_RELIABLE_EIGHT
, 1,
asZproto_params
, fzstart
, fzshutdown
, fzsendcmd
, zzgetspace
,
fzsenddata
, fzwait
, fzfile
},
{ 'g', UUCONF_RELIABLE_EIGHT
, 1,
asGproto_params
, fgstart
, fgshutdown
, fgsendcmd
, zggetspace
,
fgsenddata
, fgwait
, NULL
},
{ 'G', UUCONF_RELIABLE_EIGHT
, 1,
asGproto_params
, fbiggstart
, fgshutdown
, fgsendcmd
, zggetspace
,
fgsenddata
, fgwait
, NULL
},
{ 'j', UUCONF_RELIABLE_EIGHT
, 7,
asIproto_params
, fjstart
, fjshutdown
, fisendcmd
, zigetspace
,
fisenddata
, fiwait
, NULL
},
{ 'f', UUCONF_RELIABLE_RELIABLE
, 1,
asFproto_params
, ffstart
, ffshutdown
, ffsendcmd
, zfgetspace
,
ffsenddata
, ffwait
, fffile
},
#define CPROTOCOLS (sizeof asProtocols / sizeof asProtocols[0])
static boolean fLocked_system
;
static struct uuconf_system sLocked_system
;
/* Daemon structure holding information about the remote system (must
be global so the error handler can see it. */
static struct sdaemon sDaemon
;
static struct sconnection
*qConn
;
/* uuconf global pointer; need to close the connection after a fatal
/* This structure is passed to iuport_lock via uuconf_find_port. */
struct sconnection
*qconn
;
static void uusage
P((void));
static void uabort
P((void));
static boolean fcall
P((pointer puuconf
,
const struct uuconf_system
*qsys
,
struct uuconf_port
*qport
, boolean fifwork
,
boolean fforce
, boolean fdetach
,
static boolean fconn_call
P((struct sdaemon
*qdaemon
,
struct uuconf_port
*qport
,
struct sstatus
*qstat
, int cretry
,
static boolean fdo_call
P((struct sdaemon
*qdaemon
,
const struct uuconf_dialer
*qdialer
,
boolean
*pfcalled
, enum tstatus_type
*pterr
));
static int iuport_lock
P((struct uuconf_port
*qport
, pointer pinfo
));
static boolean flogin_prompt
P((pointer puuconf
,
struct sconnection
*qconn
));
static boolean faccept_call
P((pointer puuconf
, const char *zlogin
,
struct sconnection
*qconn
,
static void uapply_proto_params
P((pointer puuconf
, int bproto
,
struct uuconf_cmdtab
*qcmds
,
struct uuconf_proto_param
*pas
));
static boolean fsend_uucp_cmd
P((struct sconnection
*qconn
,
static char *zget_uucp_cmd
P((struct sconnection
*qconn
,
static char *zget_typed_line
P((struct sconnection
*qconn
));
/* Long getopt options. */
static const struct option asLongopts
[] = { { NULL
, 0, NULL
, 0 } };
/* -c: Whether to warn if a call is attempted at a bad time. */
boolean ftimewarn
= TRUE
;
/* -D: don't detach from controlling terminal. */
/* -e: Whether to do an endless loop of accepting calls. */
boolean fendless
= FALSE
;
/* -f: Whether to force a call despite status of previous call. */
/* -I file: configuration file name. */
const char *zconfig
= NULL
;
/* -l: Whether to give a single login prompt. */
/* -P port: port to use; in master mode, call out on this port. In
slave mode, accept logins on this port. If port not specified,
then in master mode figure it out for each system, and in slave
mode use stdin and stdout. */
const char *zport
= NULL
;
/* -q: Whether to start uuxqt when done. */
/* -r1: Whether we are the master. */
/* -s,-S system: system to call. */
const char *zsystem
= NULL
;
/* -w: Whether to wait for a call after doing one. */
struct uuconf_port
*qport
;
struct uuconf_port sport
;
while ((iopt
= getopt_long (argc
, argv
,
"cDefI:lp:qr:s:S:u:x:X:w",
asLongopts
, (int *) NULL
)) != EOF
)
/* Don't warn if a call is attempted at a bad time. */
/* Don't detach from controlling terminal. */
/* Do an endless loop of accepting calls. */
/* Force a call even if it hasn't been long enough since the last
/* Set configuration file name (default is in sysdep.h). */
if (fsysdep_other_config (optarg
))
/* Prompt for login name and password. */
/* Set mode: -r1 for master, -r0 for slave (default) */
if (strcmp (optarg
, "1") == 0)
else if (strcmp (optarg
, "0") == 0)
/* Set system name and force call like -f */
/* Some versions of uucpd invoke uucico with a -u argument
specifying the login name. I'm told it is safe to ignore
this value, although perhaps we should use it rather than
zsysdep_login_name (). */
/* Set debugging level */
iDebug
|= idebug_parse (optarg
);
/* Call out and then wait for a call in */
/* Long option found, and flag value set. */
if (fwait
&& zport
== NULL
)
ulog (LOG_ERROR
, "-w requires -e");
iuuconf
= uuconf_init (&puuconf
, (const char *) NULL
, zconfig
);
if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_FATAL
, puuconf
, iuuconf
);
iuuconf
= uuconf_debuglevel (puuconf
, &zdebug
);
if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_FATAL
, puuconf
, iuuconf
);
iDebug
|= idebug_parse (zdebug
);
/* If a port was named, get its information. */
iuuconf
= uuconf_find_port (puuconf
, zport
, (long) 0, (long) 0,
(int (*) P((struct uuconf_port
*,
if (iuuconf
== UUCONF_NOT_FOUND
)
ulog (LOG_FATAL
, "%s: Port not found", zport
);
else if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_FATAL
, puuconf
, iuuconf
);
usysdep_signal (SIGQUIT
);
usysdep_signal (SIGTERM
);
usysdep_signal (SIGPIPE
);
usysdep_initialize (puuconf
, INIT_SUID
);
ulog_to_file (puuconf
, TRUE
);
/* A system was named. Call it. */
iuuconf
= uuconf_system_info (puuconf
, zsystem
,
if (iuuconf
== UUCONF_NOT_FOUND
)
ulog (LOG_FATAL
, "%s: System not found", zsystem
);
else if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_FATAL
, puuconf
, iuuconf
);
/* Detach from the controlling terminal for the call. This
probably makes sense only on Unix. We want the modem
line to become the controlling terminal. */
|| qport
->uuconf_ttype
!= UUCONF_PORTTYPE_STDIN
))
ulog_system (sLocked_system
.uuconf_zname
);
if (sLocked_system
.uuconf_zdebug
!= NULL
)
iDebug
|= idebug_parse (sLocked_system
.uuconf_zdebug
);
if (! fsysdep_lock_system (&sLocked_system
))
ulog (LOG_ERROR
, "System already locked");
fret
= fcall (puuconf
, &sLocked_system
, qport
, FALSE
,
fforce
, fdetach
, ftimewarn
);
(void) fsysdep_unlock_system (&sLocked_system
);
ulog_system ((const char *) NULL
);
(void) uuconf_system_free (puuconf
, &sLocked_system
);
/* Call all systems which have work to do. */
iuuconf
= uuconf_system_names (puuconf
, &pznames
, 0);
if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_FATAL
, puuconf
, iuuconf
);
/* Randomize the order in which we call the systems. */
for (pz
= pznames
; *pz
!= NULL
; pz
++)
srand ((unsigned int) ixsysdep_time ((long *) NULL
));
for (i
= c
- 1; i
> 0; i
--)
iuse
= rand () % (i
+ 1);
pznames
[i
] = pznames
[iuse
];
for (pz
= pznames
; *pz
!= NULL
&& ! FGOT_SIGNAL (); pz
++)
iuuconf
= uuconf_system_info (puuconf
, *pz
,
if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
if (fsysdep_has_work (&sLocked_system
))
/* Detach from the controlling terminal. On Unix
this means that we will wind up forking a new
process for each system we call. */
|| qport
->uuconf_ttype
!= UUCONF_PORTTYPE_STDIN
))
ulog_system (sLocked_system
.uuconf_zname
);
if (sLocked_system
.uuconf_zdebug
!= NULL
)
iDebug
|= idebug_parse (sLocked_system
.uuconf_zdebug
);
if (! fsysdep_lock_system (&sLocked_system
))
ulog (LOG_ERROR
, "System already locked");
if (! fcall (puuconf
, &sLocked_system
, qport
, TRUE
,
fforce
, fdetach
, ftimewarn
))
/* Now ignore any SIGHUP that we got. */
afSignal
[INDEXSIG_SIGHUP
] = FALSE
;
(void) fsysdep_unlock_system (&sLocked_system
);
ulog_system ((const char *) NULL
);
(void) uuconf_system_free (puuconf
, &sLocked_system
);
xfree ((pointer
) pznames
);
ulog (LOG_NORMAL
, "No work");
/* If requested, wait for calls after dialing out. */
struct sconnection sconn
;
/* If a port was specified by name, we go into endless loop
mode. In this mode, we wait for calls and prompt them with
"login:" and "Password:", so that they think we are a regular
UNIX system. If we aren't in endless loop mode, we have been
called by some other system. If flogin is TRUE, we prompt
with "login:" and "Password:" a single time. */
if (! fconn_init (qport
, &sconn
))
/* We are not using standard input. Detach from the
controlling terminal, so that the port we are about to
use becomes our controlling terminal. */
&& qport
->uuconf_ttype
!= UUCONF_PORTTYPE_STDIN
)
/* If a port was given, we loop forever. */
if (fconn_lock (&sconn
, TRUE
))
ulog (LOG_ERROR
, "%s: Port already locked",
if (! fconn_open (&sconn
, (long) 0, (long) 0, TRUE
))
&& flogin_prompt (puuconf
, &sconn
))
/* Now ignore any SIGHUP that we got. */
afSignal
[INDEXSIG_SIGHUP
] = FALSE
;
(void) fsysdep_unlock_system (&sLocked_system
);
if (! fconn_reset (&sconn
))
fret
= flogin_prompt (puuconf
, &sconn
);
fret
= faccept_call (puuconf
, zsysdep_login_name (),
if (! fconn_close (&sconn
, puuconf
, (struct uuconf_dialer
*) NULL
,
(void) fconn_unlock (&sconn
);
(void) fsysdep_unlock_system (&sLocked_system
);
/* If we got a SIGTERM, perhaps because the system is going down,
don't run uuxqt. We go ahead and run it for any other signal,
since I think they indicate more temporary conditions. */
if (afSignal
[INDEXSIG_SIGTERM
])
/* Detach from the controlling terminal before starting up uuxqt,
so that it runs as a true daemon. */
if (! fsysdep_run ("uuxqt", (const char *) NULL
,
if (! fsysdep_run ("uuxqt", "-s", zsystem
))
/* Avoid complaints about not returning. */
/* Print out a usage message. */
"Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n",
"Usage: uucico [options]\n");
" -s,-S system: Call system (-S implies -f)\n");
" -f: Force call despite system status\n");
" -r state: 1 for master, 0 for slave (default)\n");
" -p port: Specify port (implies -e)\n");
" -l: prompt for login name and password\n");
" -e: Endless loop of login prompts and daemon execution\n");
" -w: After calling out, wait for incoming calls\n");
" -q: Don't start uuxqt when done\n");
" -x,-X debug: Set debugging level\n");
" -I file: Set configuration file to use\n");
#endif /* HAVE_TAYLOR_CONFIG */
/* This function is called when a LOG_FATAL error occurs. */
ulog_user ((const char *) NULL
);
(void) fconn_close (qConn
, pUuconf
, (struct uuconf_dialer
*) NULL
,
(void) fconn_unlock (qConn
);
(void) fsysdep_unlock_system (&sLocked_system
);
ulog_system ((const char *) NULL
);
/* Call another system, trying all the possible sets of calling
instructions. The qsys argument is the system to call. The qport
argument is the port to use, and may be NULL. If the fifwork
argument is TRUE, the call is only placed if there is work to be
done. If the fforce argument is TRUE, a call is forced even if not
enough time has passed since the last failed call. If the
ftimewarn argument is TRUE (the normal case), then a warning is
given if calls are not permitted at this time. */
fcall (puuconf
, qorigsys
, qport
, fifwork
, fforce
, fdetach
, ftimewarn
)
const struct uuconf_system
*qorigsys
;
struct uuconf_port
*qport
;
boolean fbadtime
, fnevertime
;
const struct uuconf_system
*qsys
;
if (! fsysdep_get_status (qorigsys
, &sstat
, (boolean
*) NULL
))
/* Make sure it's been long enough since the last failed call, and
that we haven't exceeded the maximum number of retries. Even if
we are over the limit on retries, we permit a call to be made if
24 hours have passed. This 24 hour limit is still controlled by
inow
= ixsysdep_time ((long *) NULL
);
if (qorigsys
->uuconf_cmax_retries
> 0
&& sstat
.cretries
>= qorigsys
->uuconf_cmax_retries
&& sstat
.ilast
+ 24 * 60 * 60 < inow
)
ulog (LOG_ERROR
, "Too many retries");
if (sstat
.ttype
== STATUS_COMPLETE
? sstat
.ilast
+ qorigsys
->uuconf_csuccess_wait
> inow
: sstat
.ilast
+ sstat
.cwait
> inow
)
ulog (LOG_NORMAL
, "Retry time not reached");
sDaemon
.puuconf
= puuconf
;
sDaemon
.zlocalname
= NULL
;
sDaemon
.clocal_size
= -1;
sDaemon
.cremote_size
= -1;
sDaemon
.cmax_receive
= -1;
sDaemon
.frequest_hangup
= FALSE
;
sDaemon
.fhangup_requested
= FALSE
;
for (qsys
= qorigsys
; qsys
!= NULL
; qsys
= qsys
->uuconf_qalternate
)
boolean fany
, fret
, fcalled
;
if (! qsys
->uuconf_fcall
|| qsys
->uuconf_qtimegrade
== NULL
)
/* Make sure this is a legal time to call. */
if (! ftimespan_match (qsys
->uuconf_qtimegrade
, (long *) NULL
,
/* Queue up any work there is to do. */
if (! fqueue (&sDaemon
, &fany
))
/* If we are only supposed to call if there is work, and there
isn't any work, check the next alternates. We can't give up
at this point because there might be some other alternates
with fewer restrictions on grade or file transfer size. */
fret
= fconn_call (&sDaemon
, qport
, &sstat
, cretry
, &fcalled
);
/* Now we have to dump that port so that we can aquire a new
one. On Unix this means that we will fork and get a new
process ID, so we must unlock and relock the system. */
(void) fsysdep_unlock_system (&sLocked_system
);
if (! fsysdep_lock_system (&sLocked_system
))
if (fbadtime
&& ftimewarn
)
ulog (LOG_NORMAL
, "Wrong time to call");
/* Update the status, unless the system can never be called. If
the system can never be called, there is little point to
putting in a ``wrong time to call'' message. We don't change
the number of retries, although we do set the wait until the
sstat
.ttype
= STATUS_WRONG_TIME
;
(void) fsysdep_set_status (qorigsys
, &sstat
);
/* Find a port to use when calling a system, open a connection, and
dial the system. The actual call is done in fdo_call. This
routine is responsible for opening and closing the connection. */
fconn_call (qdaemon
, qport
, qstat
, cretry
, pfcalled
)
struct uuconf_port
*qport
;
const struct uuconf_system
*qsys
;
struct uuconf_port sport
;
struct sconnection sconn
;
puuconf
= qdaemon
->puuconf
;
/* Ignore any SIGHUP signal we may have received up to this point.
This is needed on Unix because we may have gotten one from the
shell before we detached from the controlling terminal. */
afSignal
[INDEXSIG_SIGHUP
] = FALSE
;
/* If no port was specified on the command line, use any port
defined for the system. To select the system port: 1) see if
port information was specified directly; 2) see if a port was
named; 3) get an available port given the baud rate. We don't
change the system status if a port is unavailable; i.e. we don't
force the system to wait for the retry time. */
qport
= qsys
->uuconf_qport
;
if (! fconn_init (qport
, &sconn
))
if (! fconn_lock (&sconn
, FALSE
))
ulog (LOG_ERROR
, "%s: Port already locked",
iuuconf
= uuconf_find_port (puuconf
, qsys
->uuconf_zport
,
iuport_lock
, (pointer
) &s
,
if (iuuconf
== UUCONF_NOT_FOUND
)
ulog (LOG_ERROR
, "All matching ports in use");
ulog (LOG_ERROR
, "No matching ports");
else if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
(void) fconn_unlock (&sconn
);
if (! fconn_open (&sconn
, qsys
->uuconf_ibaud
, qsys
->uuconf_ihighbaud
,
terr
= STATUS_PORT_FAILED
;
struct uuconf_dialer
*qdialer
;
struct uuconf_dialer sdialer
;
enum tdialerfound tdialer
;
if (qsys
->uuconf_zalternate
== NULL
)
ulog (LOG_NORMAL
, "Calling system %s (port %s)", qsys
->uuconf_zname
,
zLdevice
== NULL
? (char *) "unknown" : zLdevice
);
ulog (LOG_NORMAL
, "Calling system %s (alternate %s, port %s)",
qsys
->uuconf_zname
, qsys
->uuconf_zalternate
,
zLdevice
== NULL
? (char *) "unknown" : zLdevice
);
if (! fconn_dial (&sconn
, puuconf
, qsys
, qsys
->uuconf_zphone
,
terr
= STATUS_DIAL_FAILED
;
if (tdialer
== DIALERFOUND_FALSE
)
fret
= fdo_call (qdaemon
, qstat
, qdialer
, pfcalled
, &terr
);
(void) fconn_close (&sconn
, puuconf
, qdialer
, fret
);
if (tdialer
== DIALERFOUND_FREE
)
(void) uuconf_dialer_free (puuconf
, &sdialer
);
DEBUG_MESSAGE2 (DEBUG_HANDSHAKE
, "Call failed: %d (%s)",
(int) terr
, azStatus
[(int) terr
]);
qstat
->ilast
= ixsysdep_time ((long *) NULL
);
qstat
->cwait
= CRETRY_WAIT (qstat
->cretries
);
qstat
->cwait
= cretry
* 60;
(void) fsysdep_set_status (qsys
, qstat
);
(void) fconn_unlock (&sconn
);
(void) uuconf_port_free (puuconf
, &sport
);
/* Do the actual work of calling another system. The qsys argument is
the system to call, the qconn argument is the connection to use,
the qstat argument holds the current status of the ssystem, and the
qdialer argument holds the dialer being used (it may be NULL). If
we log in successfully, set *pfcalled to TRUE; this is used to
distinguish a failed dial from a failure during the call. If an
error occurs *pterr is set to the status type to record. */
fdo_call (qdaemon
, qstat
, qdialer
, pfcalled
, pterr
)
const struct uuconf_dialer
*qdialer
;
enum tstatus_type
*pterr
;
const struct uuconf_system
*qsys
;
struct sconnection
*qconn
;
puuconf
= qdaemon
->puuconf
;
*pterr
= STATUS_LOGIN_FAILED
;
if (qconn
->qport
== NULL
)
zport
= qconn
->qport
->uuconf_zname
;
if (! fchat (qconn
, puuconf
, &qsys
->uuconf_schat
, qsys
,
(const struct uuconf_dialer
*) NULL
,
(const char *) NULL
, FALSE
, zport
,
istart_time
= ixsysdep_time ((long *) NULL
);
*pterr
= STATUS_HANDSHAKE_FAILED
;
/* We should now see "Shere" from the other system. Newer systems
send "Shere=foo" where foo is the remote name. */
zstr
= zget_uucp_cmd (qconn
, TRUE
);
if (strncmp (zstr
, "Shere", 5) != 0)
ulog (LOG_ERROR
, "Bad initialization string");
ulog (LOG_NORMAL
, "Login successful");
qstat
->ttype
= STATUS_TALKING
;
qstat
->ilast
= ixsysdep_time ((long *) NULL
);
if (! fsysdep_set_status (qsys
, qstat
))
/* Some UUCP packages only provide seven characters in the Shere
machine name. Others only provide fourteen. */
clen
= strlen (zheresys
);
if (clen
== 7 || clen
== 14)
icmp
= strncmp (zheresys
, qsys
->uuconf_zname
, clen
);
icmp
= strcmp (zheresys
, qsys
->uuconf_zname
);
if (qsys
->uuconf_pzalias
!= NULL
)
for (pz
= qsys
->uuconf_pzalias
; *pz
!= NULL
; pz
++)
if (clen
== 7 || clen
== 14)
icmp
= strncmp (zheresys
, *pz
, clen
);
icmp
= strcmp (zheresys
, *pz
);
ulog (LOG_ERROR
, "Called wrong system (%s)", zheresys
);
else if (zstr
[5] != '\0')
DEBUG_MESSAGE1 (DEBUG_HANDSHAKE
,
"fdo_call: Strange Shere: %s", zstr
);
/* We now send "S" name switches, where name is our UUCP name. If
we are using sequence numbers with this system, we send a -Q
argument with the sequence number. If the call-timegrade command
was used, we send a -p argument and a -vgrade= argument with the
grade to send us (we send both argument to make it more likely
that one is recognized). We always send a -N (for new) switch
indicating what new features we support. */
/* Determine the grade we should request of the other system. A
'\0' means that no restrictions have been made. */
if (! ftimespan_match (qsys
->uuconf_qcalltimegrade
, &ival
,
/* Determine the name we will call ourselves. */
if (qsys
->uuconf_zlocalname
!= NULL
)
qdaemon
->zlocalname
= qsys
->uuconf_zlocalname
;
iuuconf
= uuconf_localname (puuconf
, &qdaemon
->zlocalname
);
if (iuuconf
== UUCONF_NOT_FOUND
)
qdaemon
->zlocalname
= zsysdep_localname ();
if (qdaemon
->zlocalname
== NULL
)
else if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
zsend
= zbufalc (strlen (qdaemon
->zlocalname
) + 70);
if (! qsys
->uuconf_fsequence
)
sprintf (zsend
, "S%s -R -N0%o", qdaemon
->zlocalname
,
(unsigned int) (FEATURE_SIZES
sprintf (zsend
, "S%s -p%c -vgrade=%c -R -N0%o",
qdaemon
->zlocalname
, bgrade
, bgrade
,
(unsigned int) (FEATURE_SIZES
iseq
= ixsysdep_get_sequence (qsys
);
sprintf (zsend
, "S%s -Q%ld -R -N0%o", qdaemon
->zlocalname
, iseq
,
(unsigned int) (FEATURE_SIZES
sprintf (zsend
, "S%s -Q%ld -p%c -vgrade=%c -R -N0%o",
qdaemon
->zlocalname
, iseq
, bgrade
, bgrade
,
(unsigned int) (FEATURE_SIZES
fret
= fsend_uucp_cmd (qconn
, zsend
);
/* Now we should see ROK or Rreason where reason gives a cryptic
reason for failure. If we are talking to a counterpart, we will
get back ROKN, possibly with a feature bitfield attached. */
zstr
= zget_uucp_cmd (qconn
, TRUE
);
ulog (LOG_ERROR
, "Bad reponse to handshake string (%s)",
if (strncmp (zstr
+ 1, "OKN", sizeof "OKN" - 1) == 0)
if (zstr
[sizeof "ROKN" - 1] == '\0')
qdaemon
->ifeatures
|= FEATURE_SIZES
| FEATURE_V103
;
qdaemon
->ifeatures
|= (int) strtol (zstr
+ sizeof "ROKN" - 1,
else if (strncmp (zstr
+ 1, "OK", sizeof "OK" - 1) == 0)
if (zstr
[sizeof "ROK" - 1] != '\0')
/* SVR4 UUCP returns options following the ROK string. */
zopt
= zstr
+ sizeof "ROK" - 1;
if (isspace (b
) || b
!= '-')
qdaemon
->ifeatures
|= (FEATURE_RESTART
c
= strtol (zopt
, &zend
, 0);
if (c
> 0 && c
<= LONG_MAX
/ (long) 512)
qdaemon
->cmax_receive
= c
* (long) 512;
while (*zopt
!= '\0' && ! isspace (*zopt
))
else if (strcmp (zstr
+ 1, "CB") == 0)
ulog (LOG_NORMAL
, "Remote system will call back");
qstat
->ttype
= STATUS_COMPLETE
;
(void) fsysdep_set_status (qsys
, qstat
);
ulog (LOG_ERROR
, "Handshake failed (%s)", zstr
+ 1);
/* The slave should now send \020Pprotos\0 where protos is a list of
supported protocols. Each protocol is a single character. */
zstr
= zget_uucp_cmd (qconn
, TRUE
);
ulog (LOG_ERROR
, "Bad protocol handshake (%s)", zstr
);
/* Determine the reliability characteristics of the connection by
combining information for the port and the dialer. If we have no
information, default to a reliable eight-bit full-duplex
&& (qconn
->qport
->uuconf_ireliable
& UUCONF_RELIABLE_SPECIFIED
) != 0)
qdaemon
->ireliable
= qconn
->qport
->uuconf_ireliable
;
&& (qdialer
->uuconf_ireliable
& UUCONF_RELIABLE_SPECIFIED
) != 0)
if (qdaemon
->ireliable
!= 0)
qdaemon
->ireliable
&= qdialer
->uuconf_ireliable
;
qdaemon
->ireliable
= qdialer
->uuconf_ireliable
;
if (qdaemon
->ireliable
== 0)
qdaemon
->ireliable
= (UUCONF_RELIABLE_RELIABLE
| UUCONF_RELIABLE_FULLDUPLEX
| UUCONF_RELIABLE_SPECIFIED
);
/* Now decide which protocol to use. The system and the port may
have their own list of protocols. */
if (qsys
->uuconf_zprotocols
!= NULL
&& qconn
->qport
->uuconf_zprotocols
!= NULL
))
if (qsys
->uuconf_zprotocols
!= NULL
)
zproto
= qsys
->uuconf_zprotocols
;
zproto
= qconn
->qport
->uuconf_zprotocols
;
for (; *zproto
!= '\0'; zproto
++)
if (strchr (zstr
+ 1, *zproto
) != NULL
)
for (i
= 0; i
< CPROTOCOLS
; i
++)
if (asProtocols
[i
].bname
== *zproto
)
/* If neither the system nor the port specified a list of
protocols, we want only protocols that match the known
reliability of the dialer and the port. */
for (i
= 0; i
< CPROTOCOLS
; i
++)
ipr
= asProtocols
[i
].ireliable
;
if ((ipr
& qdaemon
->ireliable
) != ipr
)
if (strchr (zstr
+ 1, asProtocols
[i
].bname
) != NULL
)
(void) fsend_uucp_cmd (qconn
, "UN");
ulog (LOG_ERROR
, "No mutually supported protocols");
qdaemon
->qproto
= &asProtocols
[i
];
sprintf (ab
, "U%c", qdaemon
->qproto
->bname
);
if (! fsend_uucp_cmd (qconn
, ab
))
/* Run any protocol parameter commands. */
if (qdaemon
->qproto
->qcmds
!= NULL
)
if (qsys
->uuconf_qproto_params
!= NULL
)
uapply_proto_params (puuconf
, qdaemon
->qproto
->bname
,
qsys
->uuconf_qproto_params
);
&& qconn
->qport
->uuconf_qproto_params
!= NULL
)
uapply_proto_params (puuconf
, qdaemon
->qproto
->bname
,
qconn
->qport
->uuconf_qproto_params
);
&& qdialer
->uuconf_qproto_params
!= NULL
)
uapply_proto_params (puuconf
, qdaemon
->qproto
->bname
,
qdialer
->uuconf_qproto_params
);
/* Turn on the selected protocol. */
if (! (*qdaemon
->qproto
->pfstart
) (qdaemon
, &zlog
))
zlog
= zbufalc (sizeof "protocol ''" + 1);
sprintf (zlog
, "protocol '%c'", qdaemon
->qproto
->bname
);
ulog (LOG_NORMAL
, "Handshake successful (%s)", zlog
);
/* Now send the hangup message. As the caller, we send six O's
and expect to receive seven O's. We send the six O's twice to
help the other side. We don't worry about errors here. */
if (fsend_uucp_cmd (qconn
, "OOOOOO")
&& fsend_uucp_cmd (qconn
, "OOOOOO"))
/* We look for the remote hangup string to ensure that the
modem has sent out our hangup string. This is only
necessary because some versions of UUCP complain if they
don't get the hangup string. The remote site should send 7
O's, but some versions of UUCP only send 6. We look for
the string several times because supposedly some
implementations send some garbage after the last packet but
before the hangup string. */
zstr
= zget_uucp_cmd (qconn
, FALSE
);
fdone
= strstr (zstr
, "OOOOOO") != NULL
;
iend_time
= ixsysdep_time ((long *) NULL
);
ulog (LOG_NORMAL
, "Call complete (%ld seconds)",
iend_time
- istart_time
);
qstat
->ttype
= STATUS_COMPLETE
;
qstat
->ilast
= iend_time
;
(void) fsysdep_set_status (qsys
, qstat
);
/* This routine is called via uuconf_find_port when a matching port is
found. It tries to lock the port. If it fails, it returns
UUCONF_NOT_FOUND to force uuconf_find_port to continue searching
for the next matching port. */
iuport_lock (qport
, pinfo
)
struct uuconf_port
*qport
;
struct spass
*q
= (struct spass
*) pinfo
;
if (! fconn_init (qport
, q
->qconn
))
else if (! fconn_lock (q
->qconn
, FALSE
))
/* Prompt for a login name and a password, and run as the slave. */
flogin_prompt (puuconf
, qconn
)
struct sconnection
*qconn
;
DEBUG_MESSAGE0 (DEBUG_HANDSHAKE
, "flogin_prompt: Waiting for login");
if (! fconn_write (qconn
, "login: ", sizeof "login: " - 1))
zuser
= zget_typed_line (qconn
);
while (zuser
!= NULL
&& *zuser
== '\0');
if (! fconn_write (qconn
, "Password:", sizeof "Password:" - 1))
zpass
= zget_typed_line (qconn
);
iuuconf
= uuconf_callin (puuconf
, zuser
, zpass
);
if (iuuconf
== UUCONF_NOT_FOUND
)
ulog (LOG_ERROR
, "Bad login");
else if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
/* We ignore the return value of faccept_call because we really
don't care whether the call succeeded or not. We are going
to reset the port anyhow. */
(void) faccept_call (puuconf
, zuser
, qconn
, (const char **) NULL
);
/* Accept a call from a remote system. If pqsys is not NULL, *pqsys
will be set to the system that called in if known. */
faccept_call (puuconf
, zlogin
, qconn
, pzsystem
)
struct sconnection
*qconn
;
struct uuconf_port
*qport
;
struct uuconf_port sport
;
struct uuconf_dialer
*qdialer
;
struct uuconf_dialer sdialer
;
struct uuconf_system ssys
;
const struct uuconf_system
*qsys
;
const struct uuconf_system
*qany
;
ulog (LOG_NORMAL
, "Incoming call (login %s port %s)", zlogin
,
zLdevice
== NULL
? (char *) "unknown" : zLdevice
);
istart_time
= ixsysdep_time ((long *) NULL
);
/* Figure out protocol parameters determined by the port. If no
port was specified we're reading standard input, so try to get
the port name and read information from the port file. We only
use the port information to get protocol parameters; we don't
want to start treating the port as though it were a modem, for
if (qconn
->qport
!= NULL
)
zport
= qport
->uuconf_zname
;
zport
= zsysdep_port_name (&ftcp_port
);
iuuconf
= uuconf_find_port (puuconf
, zport
, (long) 0, (long) 0,
(int (*) P((struct uuconf_port
*,
if (iuuconf
== UUCONF_NOT_FOUND
)
else if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
/* If we've managed to figure out that this is a modem port, now try
to get protocol parameters from the dialer. */
if (qport
->uuconf_ttype
== UUCONF_PORTTYPE_MODEM
)
if (qport
->uuconf_u
.uuconf_smodem
.uuconf_pzdialer
!= NULL
)
zdialer
= qport
->uuconf_u
.uuconf_smodem
.uuconf_pzdialer
[0];
iuuconf
= uuconf_dialer_info (puuconf
, zdialer
, &sdialer
);
if (iuuconf
== UUCONF_SUCCESS
)
qdialer
= qport
->uuconf_u
.uuconf_smodem
.uuconf_qdialer
;
else if (qport
->uuconf_ttype
== UUCONF_PORTTYPE_TCP
|| (qport
->uuconf_ttype
== UUCONF_PORTTYPE_TLI
&& (qport
->uuconf_ireliable
& UUCONF_RELIABLE_SPECIFIED
) == 0))
sDaemon
.puuconf
= puuconf
;
sDaemon
.zlocalname
= NULL
;
sDaemon
.clocal_size
= -1;
sDaemon
.cremote_size
= -1;
sDaemon
.cmax_receive
= -1;
sDaemon
.frequest_hangup
= FALSE
;
sDaemon
.fhangup_requested
= FALSE
;
sDaemon
.bgrade
= UUCONF_GRADE_LOW
;
/* Get the local name to use. If uuconf_login_localname returns a
value, it is not always freed up, although it should be. */
iuuconf
= uuconf_login_localname (puuconf
, zlogin
, &zloc
);
if (iuuconf
== UUCONF_SUCCESS
)
sDaemon
.zlocalname
= zloc
;
else if (iuuconf
== UUCONF_NOT_FOUND
)
sDaemon
.zlocalname
= zsysdep_localname ();
if (sDaemon
.zlocalname
== NULL
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
/* Tell the remote system who we are. */
zsend
= zbufalc (strlen (sDaemon
.zlocalname
) + sizeof "Shere=");
sprintf (zsend
, "Shere=%s", sDaemon
.zlocalname
);
fret
= fsend_uucp_cmd (qconn
, zsend
);
zstr
= zget_uucp_cmd (qconn
, TRUE
);
ulog (LOG_ERROR
, "Bad introduction string");
zspace
= strchr (zstr
, ' ');
iuuconf
= uuconf_system_info (puuconf
, zstr
+ 1, &ssys
);
if (iuuconf
== UUCONF_NOT_FOUND
)
/* Run the remote.unknown script, if appropriate. */
iuuconf
= uuconf_remote_unknown (puuconf
, &zscript
);
if (iuuconf
== UUCONF_SUCCESS
)
if (! fsysdep_unknown_caller (zscript
, zstr
+ 1))
xfree ((pointer
) zscript
);
(void) fsend_uucp_cmd (qconn
, "RYou are unknown to me");
xfree ((pointer
) zscript
);
else if (iuuconf
!= UUCONF_NOT_FOUND
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
if (! funknown_system (puuconf
, zstr
+ 1, &ssys
))
(void) fsend_uucp_cmd (qconn
, "RYou are unknown to me");
ulog (LOG_ERROR
, "Call from unknown system %s", zstr
+ 1);
else if (iuuconf
!= UUCONF_SUCCESS
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
for (qsys
= &ssys
; qsys
!= NULL
; qsys
= qsys
->uuconf_qalternate
)
if (! qsys
->uuconf_fcalled
)
if (qsys
->uuconf_zcalled_login
== NULL
|| strcmp (qsys
->uuconf_zcalled_login
, "ANY") == 0)
else if (strcmp (qsys
->uuconf_zcalled_login
, zlogin
) == 0)
if (qsys
== NULL
&& qany
!= NULL
)
iuuconf
= uuconf_validate (puuconf
, qany
, zlogin
);
if (iuuconf
== UUCONF_SUCCESS
)
else if (iuuconf
!= UUCONF_NOT_FOUND
)
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
(void) fsend_uucp_cmd (qconn
, "RLOGIN");
ulog (LOG_ERROR
, "System %s used wrong login name %s",
*pzsystem
= zbufcpy (qsys
->uuconf_zname
);
ulog_system (qsys
->uuconf_zname
);
if (qsys
->uuconf_zdebug
!= NULL
)
iDebug
|= idebug_parse (qsys
->uuconf_zdebug
);
/* See if we are supposed to call the system back. This will queue
up an empty command. It would be better to actually call back
directly at this point as well. */
if (qsys
->uuconf_fcallback
)
(void) fsend_uucp_cmd (qconn
, "RCB");
ulog (LOG_NORMAL
, "Will call back");
/* Clear any existing status. */
sstat
.ttype
= STATUS_COMPLETE
;
sstat
.ilast
= ixsysdep_time ((long *) NULL
);
(void) fsysdep_set_status (qsys
, &sstat
);
ubuffree (zsysdep_spool_commands (qsys
, UUCONF_GRADE_HIGH
, 0,
(const struct scmd
*) NULL
));
/* We only permit one call at a time from a remote system. Lock it. */
if (! fsysdep_lock_system (qsys
))
(void) fsend_uucp_cmd (qconn
, "RLCK");
ulog (LOG_ERROR
, "System already locked");
/* Set the system status. We don't care what the status was before.
We also don't want to kill the conversation just because we can't
output the .Status file, so we ignore any errors. */
sstat
.ttype
= STATUS_TALKING
;
sstat
.ilast
= ixsysdep_time ((long *) NULL
);
(void) fsysdep_set_status (qsys
, &sstat
);
/* Check the arguments of the remote system, if any. */
/* Break the introduction line up into arguments. */
paz
= (char **) xmalloc ((strlen (zspace
) / 2 + 2) * sizeof (char *));
while (*zspace
!= '\0' && isspace (BUCHAR (*zspace
)))
while (*zspace
!= '\0' && ! isspace (BUCHAR (*zspace
)))
/* We are going to use getopt to parse the arguments. We
must clear optind to force getopt to reinitialize, and
clear opterr to prevent getopt from printing an error
message. This approach assumes we are using the GNU
getopt, which is distributed with the program anyhow. */
while ((iopt
= getopt (pzset
- paz
, paz
,
"N::p:Q:RU:v:x:")) != EOF
)
/* This is used to indicate support for Taylor UUCP
extensions. An plain -N mean support for size
negotiation. If -N is followed by a number (with
no intervening space), the number is a bit field
of feature flags as defined in trans.h. Note
that the argument may start with 0x for hex or 0
sDaemon
.ifeatures
|= FEATURE_SIZES
| FEATURE_V103
;
sDaemon
.ifeatures
|= (int) strtol (optarg
,
/* The argument is the lowest grade of work the
local system should send. */
if (UUCONF_GRADE_LEGAL (optarg
[0]))
sDaemon
.bgrade
= optarg
[0];
/* The conversation sequence number. */
iseq
= strtol (optarg
, (char **) NULL
, 10);
if (qsys
->uuconf_fsequence
&& iseq
!= ixsysdep_get_sequence (qsys
))
(void) fsend_uucp_cmd (qconn
, "RBADSEQ");
ulog (LOG_ERROR
, "Out of sequence call rejected");
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
/* The remote system supports file restart. */
sDaemon
.ifeatures
|= FEATURE_RESTART
;
/* The maximum file size the remote system is
prepared to received, in blocks where each block
c
= strtol (optarg
, (char **) NULL
, 0);
if (c
> 0 && c
< LONG_MAX
/ (long) 512)
sDaemon
.cmax_receive
= c
* (long) 512;
/* -vgrade=X can be used to set the lowest grade of
work the local system should send. */
if (strncmp (optarg
, "grade=", sizeof "grade=" - 1) == 0)
b
= optarg
[sizeof "grade=" - 1];
if (UUCONF_GRADE_LEGAL (b
))
iwant
= (int) strtol (optarg
, (char **) NULL
, 10);
iwant
= (1 << iwant
) - 1;
if (qsys
->uuconf_zmax_remote_debug
!= NULL
)
iwant
&= idebug_parse (qsys
->uuconf_zmax_remote_debug
);
if ((iDebug
| iwant
) != iDebug
)
ulog (LOG_NORMAL
, "Setting debugging mode to 0%o",
if (qsys
->uuconf_fsequence
&& ! fgotseq
)
(void) fsend_uucp_cmd (qconn
, "RBADSEQ");
ulog (LOG_ERROR
, "No sequence number (call rejected)");
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
/* We recognized the system, and the sequence number (if any) was
OK. Send an ROK, and send a list of protocols. If we got the -N
switch, send ROKN to confirm it; if the -N switch was followed by
a feature bitfield, return our own feature bitfield. */
if ((sDaemon
.ifeatures
& FEATURE_RESTART
) == 0)
/* We got -R without -N, so assume that this is SVR4 UUCP.
SVR4 UUCP expects ROK -R to signal support for file
sDaemon
.ifeatures
|= FEATURE_SVR4
| FEATURE_SIZES
;
else if ((sDaemon
.ifeatures
& FEATURE_V103
) != 0)
(unsigned int) (FEATURE_SIZES
if (! fsend_uucp_cmd (qconn
, zreply
))
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
/* Determine the reliability of the connection based on the
reliability of the port and the dialer. If we have no
information, default to a reliable eight-bit full-duplex
sDaemon
.ireliable
= (UUCONF_RELIABLE_SPECIFIED
| UUCONF_RELIABLE_ENDTOEND
| UUCONF_RELIABLE_RELIABLE
| UUCONF_RELIABLE_FULLDUPLEX
);
&& (qport
->uuconf_ireliable
& UUCONF_RELIABLE_SPECIFIED
) != 0)
sDaemon
.ireliable
= qport
->uuconf_ireliable
;
&& (qdialer
->uuconf_ireliable
& UUCONF_RELIABLE_SPECIFIED
) != 0)
if (sDaemon
.ireliable
!= 0)
sDaemon
.ireliable
&= qdialer
->uuconf_ireliable
;
sDaemon
.ireliable
= qdialer
->uuconf_ireliable
;
if (sDaemon
.ireliable
== 0)
sDaemon
.ireliable
= (UUCONF_RELIABLE_RELIABLE
| UUCONF_RELIABLE_FULLDUPLEX
| UUCONF_RELIABLE_SPECIFIED
);
if (qsys
->uuconf_zprotocols
!= NULL
||
(qport
!= NULL
&& qport
->uuconf_zprotocols
!= NULL
))
if (qsys
->uuconf_zprotocols
!= NULL
)
zprotos
= qsys
->uuconf_zprotocols
;
zprotos
= qport
->uuconf_zprotocols
;
zsend
= zbufalc (strlen (zprotos
) + 2);
sprintf (zsend
, "P%s", zprotos
);
zsend
= zbufalc (CPROTOCOLS
+ 2);
/* If the system did not specify a list of protocols, we want
only protocols that match the known reliability of the dialer
for (i
= 0; i
< CPROTOCOLS
; i
++)
ipr
= asProtocols
[i
].ireliable
;
if ((ipr
& sDaemon
.ireliable
) != ipr
)
*zset
++ = asProtocols
[i
].bname
;
fret
= fsend_uucp_cmd (qconn
, zsend
);
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
/* The master will now send back the selected protocol. */
zstr
= zget_uucp_cmd (qconn
, TRUE
);
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
if (zstr
[0] != 'U' || zstr
[2] != '\0')
ulog (LOG_ERROR
, "Bad protocol response string");
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
ulog (LOG_ERROR
, "No supported protocol");
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
for (i
= 0; i
< CPROTOCOLS
; i
++)
if (asProtocols
[i
].bname
== zstr
[1])
ulog (LOG_ERROR
, "No supported protocol");
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
sDaemon
.qproto
= &asProtocols
[i
];
/* Run the chat script for when a call is received. */
if (! fchat (qconn
, puuconf
, &qsys
->uuconf_scalled_chat
, qsys
,
(const struct uuconf_dialer
*) NULL
, (const char *) NULL
,
FALSE
, zport
, iconn_baud (qconn
)))
sstat
.ttype
= STATUS_FAILED
;
sstat
.ilast
= ixsysdep_time ((long *) NULL
);
(void) fsysdep_set_status (qsys
, &sstat
);
/* Run any protocol parameter commands. */
if (sDaemon
.qproto
->qcmds
!= NULL
)
if (qsys
->uuconf_qproto_params
!= NULL
)
uapply_proto_params (puuconf
, sDaemon
.qproto
->bname
,
qsys
->uuconf_qproto_params
);
&& qport
->uuconf_qproto_params
!= NULL
)
uapply_proto_params (puuconf
, sDaemon
.qproto
->bname
,
qport
->uuconf_qproto_params
);
&& qdialer
->uuconf_qproto_params
!= NULL
)
uapply_proto_params (puuconf
, sDaemon
.qproto
->bname
,
qdialer
->uuconf_qproto_params
);
/* We don't need the dialer information any more. */
(void) uuconf_dialer_free (puuconf
, &sdialer
);
/* Get any jobs queued for the system, and turn on the selected
if (! fqueue (&sDaemon
, (boolean
*) NULL
)
|| ! (*sDaemon
.qproto
->pfstart
) (&sDaemon
, &zlog
))
sstat
.ttype
= STATUS_FAILED
;
sstat
.ilast
= ixsysdep_time ((long *) NULL
);
(void) fsysdep_set_status (qsys
, &sstat
);
zlog
= zbufalc (sizeof "protocol ''" + 1);
sprintf (zlog
, "protocol '%c'", sDaemon
.qproto
->bname
);
zgrade
= zbufalc (sizeof "grade " + 1);
if (sDaemon
.bgrade
== UUCONF_GRADE_LOW
)
sprintf (zgrade
, "grade %c ", sDaemon
.bgrade
);
/* If we are using HAVE_HDB_LOGGING, then the previous ``incoming
call'' message went to the general log, since we didn't know the
system name at that point. In that case, we repeat the port and
ulog (LOG_NORMAL
, "Handshake successful (login %s port %s %s%s)",
zLdevice
== NULL
? "unknown" : zLdevice
,
#else /* ! HAVE_HDB_LOGGING */
ulog (LOG_NORMAL
, "Handshake successful (%s%s)", zgrade
, zlog
);
#endif /* ! HAVE_HDB_LOGGING */
/* Hangup. As the answerer, we send seven O's and expect to
receive six O's. We send the seven O's twice to help the other
side. We don't worry about errors here. */
if (fsend_uucp_cmd (qconn
, "OOOOOOO")
&& fsend_uucp_cmd (qconn
, "OOOOOOO"))
/* We look for the remote hangup string to ensure that the
modem has sent out our hangup string. This is only
necessary because some versions of UUCP complain if they
don't get the hangup string. We look for the string
several times because supposedly some implementations send
some garbage after the last packet but before the hangup
zstr
= zget_uucp_cmd (qconn
, FALSE
);
fdone
= strstr (zstr
, "OOOOOO") != NULL
;
iend_time
= ixsysdep_time ((long *) NULL
);
ulog (LOG_NORMAL
, "Call complete (%ld seconds)",
iend_time
- istart_time
);
sstat
.ttype
= STATUS_COMPLETE
;
sstat
.ttype
= STATUS_FAILED
;
(void) fsysdep_set_status (qsys
, &sstat
);
(void) uuconf_system_free (puuconf
, &ssys
);
(void) uuconf_port_free (puuconf
, &sport
);
/* Apply protocol parameters, once we know the protocol. */
uapply_proto_params (puuconf
, bproto
, qcmds
, pas
)
struct uuconf_cmdtab
*qcmds
;
struct uuconf_proto_param
*pas
;
struct uuconf_proto_param
*qp
;
for (qp
= pas
; qp
->uuconf_bproto
!= '\0'; qp
++)
if (qp
->uuconf_bproto
== bproto
)
struct uuconf_proto_param_entry
*qe
;
for (qe
= qp
->uuconf_qentries
; qe
->uuconf_cargs
> 0; qe
++)
iuuconf
= uuconf_cmd_args (puuconf
, qe
->uuconf_cargs
,
qe
->uuconf_pzargs
, qcmds
,
(uuconf_cmdtabfn
) NULL
, 0,
if (UUCONF_ERROR_VALUE (iuuconf
) != UUCONF_SUCCESS
)
ulog (LOG_ERROR
, "Error in %c protocol parameters",
ulog_uuconf (LOG_ERROR
, puuconf
, iuuconf
);
/* Send a string to the other system beginning with a DLE
character and terminated with a null byte. This is only
used when no protocol is in force. */
fsend_uucp_cmd (qconn
, z
)
struct sconnection
*qconn
;
DEBUG_MESSAGE1 (DEBUG_HANDSHAKE
, "fsend_uucp_cmd: Sending \"%s\"", z
);
memcpy (zalc
+ 1, z
, cwrite
- 1);
fret
= fconn_write (qconn
, zalc
, cwrite
);
/* Get a UUCP command beginning with a DLE character and ending with a
null byte. This is only used when no protocol is in force. This
implementation has the potential of being seriously slow. It also
doesn't have any real error recovery. The frequired argument is
passed as TRUE if we need the string; we don't care that much if
we're closing down the connection anyhow. */
#define CSHORTTIMEOUT (10)
zget_uucp_cmd (qconn
, frequired
)
struct sconnection
*qconn
;
iendtime
= ixsysdep_time ((long *) NULL
);
iendtime
+= CSHORTTIMEOUT
;
if (FDEBUGGING (DEBUG_HANDSHAKE
))
ulog (LOG_DEBUG_START
, "zget_uucp_cmd: Got \"");
iDebug
&=~ (DEBUG_INCOMING
| DEBUG_PORT
);
while ((ctimeout
= (int) (iendtime
- ixsysdep_time ((long *) NULL
))) > 0)
b
= breceive_char (qconn
, ctimeout
, frequired
);
/* Now b == -1 on timeout, -2 on error. */
if (FDEBUGGING (DEBUG_HANDSHAKE
))
ulog (LOG_DEBUG_END
, "\" (%s)",
b
== -1 ? "timeout" : "error");
if (b
== -1 && frequired
)
ulog (LOG_ERROR
, "Timeout");
/* Apparently some systems use parity on these strings, so we
strip the parity bit. This may need to be configurable at
some point, although only if system names can have eight bit
if (! isprint (BUCHAR (b
)))
if (FDEBUGGING (DEBUG_HANDSHAKE
))
ulog (LOG_DEBUG_END
, "\"");
ulog (LOG_DEBUG_START
, "zget_uucp_cmd: Got \"");
(void) cdebug_char (ab
, b
);
ulog (LOG_DEBUG_CONTINUE
, "%s", ab
);
/* If we see another DLE, something has gone wrong; continue
as though this were the first one we saw. */
/* Some systems send a trailing \n on the Shere line. As far as
I can tell this line can never contain a \n, so this
modification should be safe enough. */
if (b
== '\r' || b
== '\n')
memcpy (znew
, zalc
, cgot
);
if (FDEBUGGING (DEBUG_HANDSHAKE
))
ulog (LOG_DEBUG_END
, "\"");
if (FDEBUGGING (DEBUG_HANDSHAKE
))
ulog (LOG_DEBUG_END
, "\" (timeout)");
ulog (LOG_ERROR
, "Timeout");
/* Read a sequence of characters up to a newline or carriage return, and
return the line without the line terminating character. */
struct sconnection
*qconn
;
if (FDEBUGGING (DEBUG_CHAT
))
ulog (LOG_DEBUG_START
, "zget_typed_line: Got \"");
iDebug
&=~ (DEBUG_INCOMING
| DEBUG_PORT
);
b
= breceive_char (qconn
, CTIMEOUT
, FALSE
);
/* Now b == -1 on timeout, -2 on error. */
if (b
== -2 || FGOT_SIGNAL ())
if (FDEBUGGING (DEBUG_CHAT
))
ulog (LOG_DEBUG_END
, "\" (error)");
if (FDEBUGGING (DEBUG_CHAT
))
ulog (LOG_DEBUG_END
, "\"");
ulog (LOG_DEBUG_START
, "zget_typed_line: Got \"");
(void) cdebug_char (ab
, b
);
ulog (LOG_DEBUG_CONTINUE
, "%s", ab
);
memcpy (znew
, zalc
, cgot
);
if (b
== '\r' || b
== '\n')
if (FDEBUGGING (DEBUG_CHAT
))
ulog (LOG_DEBUG_END
, "\"");