/* tstuu.c Test the uucp package on a UNIX system. 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 AIRS, P.O. Box 520, Waltham, MA 02254. $Log: tstuu.c,v $ Revision 1.1.1.1 1993/03/21 09:45:37 cgd initial import of 386bsd-0.1 sources * Revision 1.2 1992/05/13 05:42:07 rich * ported to 386bsd * * Revision 1.1 1992/05/10 20:03:07 rich * Initial revision * Revision 1.52 1992/04/02 23:02:40 ian Conditionally declare times Revision 1.51 1992/03/28 04:29:19 ian Roberto Biancardi: define SIGCHLD as SIGCLD if necessary Revision 1.50 1992/03/16 22:22:35 ian Adjusted external declarations Revision 1.49 1992/03/15 01:54:46 ian All execs are now done in isspawn, all waits are done in iswait Revision 1.48 1992/03/12 19:54:43 ian Debugging based on types rather than number Revision 1.47 1992/03/08 17:02:24 ian Ted Lindgreen: don't include if it's not there Revision 1.46 1992/03/04 15:38:19 ian Roberto Biancardi: use poll if we haven't got select Revision 1.45 1992/03/04 01:40:51 ian Thomas Fischer: tweaked a bit for the NeXT Revision 1.44 1992/02/27 05:40:54 ian T. William Wells: detach from controlling terminal, handle signals safely Revision 1.43 1992/02/24 20:07:43 ian John Theus: some systems don't have Revision 1.42 1992/02/24 04:58:47 ian Only permit files to be received into directories that are world-writeable Revision 1.41 1992/02/23 03:26:51 ian Overhaul to use automatic configure shell script Revision 1.40 1992/02/08 03:54:18 ian Include only in , added 1992 copyright Revision 1.39 1992/01/29 04:27:11 ian Jay Vassos-Libove: removed some conflicting declarations Revision 1.38 1992/01/19 02:53:05 ian Mike Park: don't sleep when buffer is full; it's too slow Revision 1.37 1992/01/16 16:32:44 ian Mike Park: ioctl is sometimes declared varadic, so we can't declare it Revision 1.36 1992/01/15 21:06:11 ian Mike Park: some systems can't include and together Revision 1.35 1992/01/15 20:48:41 ian Mike Park: removed prototype for times Revision 1.34 1992/01/15 20:02:05 ian Mike Park: sh on NeXT interprets leading ~ (incredible, isn't it) Revision 1.33 1992/01/15 19:40:35 ian Mike Park: handle HAVE_UNION_WAIT correctly and completely Revision 1.32 1992/01/13 19:38:16 ian Chip Salzenberg: can't declare execl, since it is varadic Revision 1.31 1992/01/13 06:11:39 ian David Nugent: can't declare open or fcntl Revision 1.30 1992/01/13 05:53:04 ian Mike Park: added HAVE_WAITPID and HAVE_WAIT4 configuration parameters Revision 1.29 1992/01/13 05:37:20 ian Mike Park: use IPUBLIC_DIRECTORY_MODE rather than S_ macros Revision 1.28 1991/12/29 04:04:18 ian Added a bunch of extern definitions Revision 1.27 1991/12/29 00:55:23 ian Monty Solomon: added HAVE_UNION_WAIT Revision 1.26 1991/12/28 06:35:05 ian Use TIMES_TICK rather than CLK_TCK Revision 1.25 1991/12/28 06:10:50 ian Added HAVE_STRCHR and HAVE_INDEX to conf.h Revision 1.24 1991/12/28 04:05:13 ian Create spool directories Revision 1.23 1991/12/28 03:49:23 ian Added HAVE_MEMFNS and HAVE_BFNS; changed uses of memset to bzero Revision 1.22 1991/12/22 22:14:19 ian Monty Solomon: added HAVE_UNISTD_H configuration parameter Revision 1.21 1991/12/21 22:07:47 ian John Theus: don't warn if port file does not exist Revision 1.20 1991/12/19 04:25:57 ian Terry Gardner: configuration parameter to not use both NONBLOCK and NDELAY Revision 1.19 1991/12/19 03:55:40 ian Give the uucico processes a chance to die on their own Revision 1.18 1991/12/18 05:12:00 ian Added -l option to uucico to prompt for login name once and then exit Revision 1.17 1991/12/17 22:21:19 ian Sleep before printing login to wait until input has been flushed Revision 1.16 1991/12/17 04:55:01 ian David Nugent: ignore SIGHUP in uucico and uuxqt Revision 1.15 1991/12/11 04:21:37 ian Arne Ludwig: merge in Arne Ludwig's patches for V2 and BNU style logging Revision 1.14 1991/12/07 02:57:28 ian Allow failure message to be sent from uux test Revision 1.13 1991/12/01 19:58:01 ian Don't use the not-very-portable fd_set typedef at all Revision 1.12 1991/12/01 19:41:00 ian Don't read V2 or BNU configuration files while testing Revision 1.11 1991/12/01 03:29:30 ian Bob Izenberg: get definitions for EAGAIN and EWOULDBLOCK Revision 1.10 1991/12/01 02:23:12 ian Niels Baggesen: don't multiply include Revision 1.9 1991/12/01 02:12:02 ian David Nugent: some systems don't define O_NDELAY Revision 1.8 1991/11/26 02:13:48 ian Bob Denny: Add definitions for FD_SET, FD_ZERO and FD_ISSET Revision 1.7 1991/11/26 01:45:42 ian Marty Shannon: configuration option to not include Revision 1.6 1991/11/21 22:17:06 ian Add version string, print version when printing usage Revision 1.5 1991/11/14 21:07:15 ian Create port file and add protocol command for second system Revision 1.4 1991/11/12 19:47:04 ian Add called-chat set of commands to run a chat script on an incoming call Revision 1.3 1991/11/11 23:47:24 ian Added chat-program to run a program to do a chat script Revision 1.2 1991/11/11 04:21:16 ian Added 'f' protocol Revision 1.1 1991/09/10 19:40:31 ian Initial revision */ #include "uucp.h" #if USE_RCS_ID char tstuu_rcsid[] = "$Id: tstuu.c,v 1.1.1.1 1993/03/21 09:45:37 cgd Exp $"; #endif #include #include #include #if USE_STDIO && HAVE_UNISTD_H #include #endif #include "sysdep.h" #include #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_SELECT #include #endif #if HAVE_POLL #include #include #endif #if HAVE_FCNTL_H #include #else #if HAVE_SYS_FILE_H #include #endif #endif #ifndef O_RDONLY #define O_RDONLY 0 #define O_WRONLY 1 #define O_RDWR 2 #endif #if HAVE_TIME_H && (HAVE_SYS_TIME_AND_TIME_H || ! HAVE_SELECT) #include #endif #if HAVE_SYS_WAIT_H #include #endif #if HAVE_UNION_WAIT typedef union wait wait_status; #else typedef int wait_status; #endif #include "getopt.h" /* Get definitions for both O_NONBLOCK and O_NDELAY. */ #ifndef O_NDELAY #ifdef FNDELAY #define O_NDELAY FNDELAY #else /* ! defined (FNDELAY) */ #define O_NDELAY 0 #endif /* ! defined (FNDELAY) */ #endif /* ! defined (O_NDELAY) */ #ifndef O_NONBLOCK #ifdef FNBLOCK #define O_NONBLOCK FNBLOCK #else /* ! defined (FNBLOCK) */ #define O_NONBLOCK 0 #endif /* ! defined (FNBLOCK) */ #endif /* ! defined (O_NONBLOCK) */ #if O_NDELAY == 0 && O_NONBLOCK == 0 #error No way to do nonblocking I/O #endif /* If we can define them both together, do so. This is because some ancient drivers on some systems appear to look for one but not the other. Otherwise just use O_NONBLOCK. */ #if COMBINED_UNBLOCK #define FILE_UNBLOCKED (O_NDELAY | O_NONBLOCK) #else #define FILE_UNBLOCKED O_NONBLOCK #endif /* Get definitions for both EAGAIN and EWOULDBLOCK. */ #ifndef EAGAIN #ifndef EWOULDBLOCK #define EAGAIN (-1) #define EWOULDBLOCK (-1) #else /* defined (EWOULDBLOCK) */ #define EAGAIN EWOULDBLOCK #endif /* defined (EWOULDBLOCK) */ #else /* defined (EAGAIN) */ #ifndef EWOULDBLOCK #define EWOULDBLOCK EAGAIN #endif /* ! defined (EWOULDBLOCK) */ #endif /* defined (EAGAIN) */ /* Make sure we have a CLK_TCK definition, even if it makes no sense. This is in case TIMES_TICK is defined as CLK_TCK. */ #ifndef CLK_TCK #define CLK_TCK (60) #endif /* Don't try too hard to get a TIMES_TICK value; it doesn't matter that much. */ #if TIMES_TICK == 0 #undef TIMES_TICK #define TIMES_TICK CLK_TCK #endif #if TIMES_DECLARATION_OK extern long times (); #endif #ifndef SIGCHLD #define SIGCHLD SIGCLD #endif #define ZUUCICO_CMD "login uucp" #define UUCICO_EXECL "/bin/login", "login", "uucp" #if ! HAVE_SELECT && ! HAVE_POLL #error You need select or poll #endif /* External functions. */ extern int close (), dup2 (), access (); extern int read (), write (), unlink (); extern int fclose (), fflush (), rand (), system (); extern pid_t fork (); #if HAVE_SELECT extern int select (); #endif #if HAVE_POLL extern int poll (); #endif #if ! HAVE_WAITPID && HAVE_WAIT4 extern pid_t wait4 (); #endif #if ! HAVE_REMOVE #undef remove #define remove unlink #endif /* Local functions. */ static void umake_file P((const char *zfile, int cextra)); static void uprepare_test P((int itest, boolean fcall_uucico, const char *zsys)); static void ucheck_file P((const char *zfile, const char *zerr, int cextra)); static void ucheck_test P((int itest, boolean fcall_uucico)); static void utransfer P((int ofrom, int oto, int otoslave, int *pc)); static SIGtype uchild P((int isig)); static int cpshow P((char *z, int bchar)); static void uchoose P((int *po1, int *po2)); static boolean fwritable P((int o)); static void xsystem P((const char *zcmd)); static char *zDebug; static int iTest; static boolean fCall_uucico; static int iPercent; static pid_t iPid1, iPid2; static int cFrom1, cFrom2; static char abLogout1[sizeof "tstout /dev/ptyp0"]; static char abLogout2[sizeof "tstout /dev/ptyp0"]; static char *zProtocols; int main (argc, argv) int argc; char **argv; { int iopt; const char *zcmd1, *zcmd2; const char *zpty; const char *zsys; char abpty1[sizeof "/dev/ptyp0"]; char abpty2[sizeof "/dev/ptyp0"]; char *zptyname; int omaster1, oslave1, omaster2, oslave2; zcmd1 = NULL; zcmd2 = NULL; zsys = "test2"; while ((iopt = getopt (argc, argv, "c:p:s:t:ux:1:2:")) != EOF) { switch (iopt) { case 'c': zProtocols = optarg; break; case 'p': iPercent = atoi (optarg); break; case 's': zsys = optarg; break; case 't': iTest = atoi (optarg); break; case 'u': fCall_uucico = TRUE; break; case 'x': zDebug = optarg; break; case '1': zcmd1 = optarg; break; case '2': zcmd2 = optarg; break; default: fprintf (stderr, "Taylor UUCP version %s, copyright (C) 1991, 1992 Ian Lance Taylor\n", abVersion); fprintf (stderr, "Usage: tstuu [-x] [-t #] [-u] [-1 cmd] [-2 cmd]\n"); exit (EXIT_FAILURE); } } if (fCall_uucico && zcmd2 == NULL) zcmd2 = ZUUCICO_CMD; uprepare_test (iTest, fCall_uucico, zsys); (void) remove ("/usr/tmp/tstuu/spool1/core"); (void) remove ("/usr/tmp/tstuu/spool2/core"); omaster1 = -1; oslave1 = -1; omaster2 = -1; oslave2 = -1; zptyname = abpty1; for (zpty = "pqrs"; *zpty != '\0'; ++zpty) { int ipty; for (ipty = 0; ipty < 16; ipty++) { int om, os; FILE *e; sprintf (zptyname, "/dev/pty%c%c", *zpty, "0123456789abcdef"[ipty]); om = open (zptyname, O_RDWR); if (om < 0) continue; zptyname[5] = 't'; os = open (zptyname, O_RDWR); if (os < 0) { (void) close (om); continue; } if (omaster1 == -1) { omaster1 = om; oslave1 = os; e = fopen ("/usr/tmp/tstuu/pty1", "w"); if (e == NULL) { perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "%s", zptyname + 5); if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } zptyname = abpty2; } else { omaster2 = om; oslave2 = os; e = fopen ("/usr/tmp/tstuu/pty2", "w"); if (e == NULL) { perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "%s", zptyname + 5); if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } break; } } if (omaster1 != -1 && omaster2 != -1) break; } if (omaster2 == -1) { fprintf (stderr, "No pseudo-terminals available\n"); exit (EXIT_FAILURE); } /* Make sure we can or these into an int for the select call. Most systems could use 31 instead of 15, but it should never be a problem. */ if (omaster1 > 15 || omaster2 > 15) { fprintf (stderr, "File descriptors are too large\n"); exit (EXIT_FAILURE); } /* Prepare to log out the command if it is a login command. On Ultrix 4.0 uucico can only be run from login for some reason. */ if (zcmd1 == NULL || strncmp (zcmd1, "login", sizeof "login" - 1) != 0) abLogout1[0] = '\0'; else sprintf (abLogout1, "tstout %s", abpty1); if (zcmd2 == NULL || strncmp (zcmd2, "login", sizeof "login" - 1) != 0) abLogout2[0] = '\0'; else sprintf (abLogout2, "tstout %s", abpty2); iPid1 = fork (); if (iPid1 < 0) { perror ("fork"); exit (EXIT_FAILURE); } else if (iPid1 == 0) { if (close (0) < 0 || close (1) < 0 || close (omaster1) < 0 || close (omaster2) < 0 || close (oslave2) < 0) perror ("close"); if (dup2 (oslave1, 0) < 0 || dup2 (oslave1, 1) < 0) perror ("dup2"); if (close (oslave1) < 0) perror ("close"); if (zDebug != NULL) fprintf (stderr, "About to exec first process\n"); if (zcmd1 != NULL) exit (system ((char *) zcmd1)); else { (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config1", "-q", "-S", zsys, "-pstdin", (const char *) NULL); fprintf (stderr, "execl failed\n"); exit (EXIT_FAILURE); } } iPid2 = fork (); if (iPid2 < 0) { perror ("fork"); kill (iPid1, SIGTERM); exit (EXIT_FAILURE); } else if (iPid2 == 0) { if (close (0) < 0 || close (1) < 0 || close (omaster1) < 0 || close (oslave1) < 0 || close (omaster2) < 0) perror ("close"); if (dup2 (oslave2, 0) < 0 || dup2 (oslave2, 1) < 0) perror ("dup2"); if (close (oslave2) < 0) perror ("close"); if (zDebug != NULL) fprintf (stderr, "About to exec second process\n"); if (fCall_uucico) { (void) execl (UUCICO_EXECL, (const char *) NULL); fprintf (stderr, "execl failed\n"); exit (EXIT_FAILURE); } else if (zcmd2 != NULL) exit (system ((char *) zcmd2)); else { (void) execl ("uucico", "uucico", "-I", "/usr/tmp/tstuu/Config2", "-lq", (const char *)NULL); fprintf (stderr, "execl failed\n"); exit (EXIT_FAILURE); } } signal (SIGCHLD, uchild); (void) fcntl (omaster1, F_SETFL, FILE_UNBLOCKED); (void) fcntl (omaster2, F_SETFL, FILE_UNBLOCKED); while (TRUE) { int o1, o2; o1 = omaster1; o2 = omaster2; uchoose (&o1, &o2); if (o1 == -1 && o2 == -1) { if (zDebug != NULL) fprintf (stderr, "Five second pause\n"); continue; } if (o1 != -1 && fwritable (omaster2)) utransfer (omaster1, omaster2, oslave2, &cFrom1); if (o2 != - 1 && fwritable (omaster1)) utransfer (omaster2, omaster1, oslave1, &cFrom2); } /*NOTREACHED*/ } /* When a child dies, kill them both. */ static SIGtype uchild (isig) int isig; { struct tms sbase, s1, s2; signal (SIGCHLD, SIG_DFL); /* Give the processes a chance to die on their own. */ sleep (1); (void) kill (iPid1, SIGTERM); (void) kill (iPid2, SIGTERM); (void) times (&sbase); #if HAVE_WAITPID (void) waitpid (iPid1, (wait_status *) NULL, 0); #else /* ! HAVE_WAITPID */ #if HAVE_WAIT4 (void) wait4 (iPid1, (wait_status *) NULL, 0, (struct rusage *) NULL); #else /* ! HAVE_WAIT4 */ (void) wait ((wait_status *) NULL); #endif /* ! HAVE_WAIT4 */ #endif /* ! HAVE_WAITPID */ (void) times (&s1); #if HAVE_WAITPID (void) waitpid (iPid2, (wait_status *) NULL, 0); #else /* ! HAVE_WAITPID */ #if HAVE_WAIT4 (void) wait4 (iPid2, (wait_status *) NULL, 0, (struct rusage *) NULL); #else /* ! HAVE_WAIT4 */ (void) wait ((wait_status *) NULL); #endif /* ! HAVE_WAIT4 */ #endif /* ! HAVE_WAITPID */ (void) times (&s2); fprintf (stderr, " First child: user: %g; system: %g\n", (double) (s1.tms_cutime - sbase.tms_cutime) / (double) TIMES_TICK, (double) (s1.tms_cstime - sbase.tms_cstime) / (double) TIMES_TICK); fprintf (stderr, "Second child: user: %g; system: %g\n", (double) (s2.tms_cutime - s1.tms_cutime) / (double) TIMES_TICK, (double) (s2.tms_cstime - s1.tms_cstime) / (double) TIMES_TICK); ucheck_test (iTest, fCall_uucico); if (abLogout1[0] != '\0') { if (zDebug != NULL) fprintf (stderr, "Executing %s\n", abLogout1); (void) system (abLogout1); } if (abLogout2[0] != '\0') { if (zDebug != NULL) fprintf (stderr, "Executing %s\n", abLogout2); (void) system (abLogout2); } fprintf (stderr, "Wrote %d bytes from 1 to 2\n", cFrom1); fprintf (stderr, "Wrote %d bytes from 2 to 1\n", cFrom2); if (access ("/usr/tmp/tstuu/spool1/core", R_OK) == 0) fprintf (stderr, "core file 1 exists\n"); if (access ("/usr/tmp/tstuu/spool2/core", R_OK) == 0) fprintf (stderr, "core file 2 exists\n"); exit (EXIT_SUCCESS); } /* Open a file without error. */ static FILE *xfopen P((const char *zname, const char *zmode)); static FILE * xfopen (zname, zmode) const char *zname; const char *zmode; { FILE *eret; eret = fopen (zname, zmode); if (eret == NULL) { perror (zname); exit (EXIT_FAILURE); } return eret; } /* Close a file without error. */ static void xfclose P((FILE *e)); static void xfclose (e) FILE *e; { if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } } /* Create a test file. */ static void umake_file (z, c) const char *z; int c; { int i; FILE *e; e = xfopen (z, "w"); for (i = 0; i < 256; i++) { int i2; for (i2 = 0; i2 < 256; i2++) putc (i, e); } for (i = 0; i < c; i++) putc (i, e); xfclose (e); } /* Check a test file. */ static void ucheck_file (z, zerr, c) const char *z; const char *zerr; int c; { int i; FILE *e; e = xfopen (z, "r"); for (i = 0; i < 256; i++) { int i2; for (i2 = 0; i2 < 256; i2++) { int bread; bread = getc (e); if (bread == EOF) { fprintf (stderr, "%s: Unexpected EOF at position %d,%d\n", zerr, i, i2); xfclose (e); return; } if (bread != i) fprintf (stderr, "%s: At position %d,%d got %d expected %d\n", zerr, i, i2, bread, i); } } for (i = 0; i < c; i++) { int bread; bread = getc (e); if (bread == EOF) { fprintf (stderr, "%s: Unexpected EOF at extra %d\n", zerr, i); xfclose (e); return; } if (bread != i) fprintf (stderr, "%s: At extra %d got %d expected %d\n", zerr, i, bread, i); } if (getc (e) != EOF) fprintf (stderr, "%s: File is too long", zerr); xfclose (e); } /* Prepare all the configuration files for testing. */ static void uprepare_test (itest, fcall_uucico, zsys) int itest; boolean fcall_uucico; const char *zsys; { FILE *e; const char *zuucp1, *zuucp2; const char *zuux1, *zuux2; char ab[1000]; const char *zfrom; const char *zto; /* We must make /usr/tmp/tstuu world writeable or we won't be able to receive files into it. */ (void) umask (0); #ifndef S_IWOTH #define S_IWOTH 02 #endif if (mkdir ((char *) "/usr/tmp/tstuu", IPUBLIC_DIRECTORY_MODE | S_IWOTH) != 0 && errno != EEXIST) { perror ("mkdir"); exit (EXIT_FAILURE); } if (mkdir ((char *) "/usr/tmp/tstuu/spool1", IPUBLIC_DIRECTORY_MODE) != 0 && errno != EEXIST) { perror ("mkdir"); exit (EXIT_FAILURE); } if (mkdir ((char *) "/usr/tmp/tstuu/spool2", IPUBLIC_DIRECTORY_MODE) != 0 && errno != EEXIST) { perror ("mkdir"); exit (EXIT_FAILURE); } e = xfopen ("/usr/tmp/tstuu/Config1", "w"); fprintf (e, "# First test configuration file\n"); fprintf (e, "nodename test1\n"); fprintf (e, "spool /usr/tmp/tstuu/spool1\n"); fprintf (e, "sysfile /usr/tmp/tstuu/System1\n"); fprintf (e, "sysfile /usr/tmp/tstuu/System1.2\n"); fprintf (e, "portfile /usr/tmp/tstuu/Port1\n"); (void) remove ("/usr/tmp/tstuu/Log1"); #if ! HAVE_BNU_LOGGING fprintf (e, "logfile /usr/tmp/tstuu/Log1\n"); #else fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log1/%s/%s"); #endif fprintf (e, "statfile /usr/tmp/tstuu/Stats1\n"); fprintf (e, "debugfile /usr/tmp/tstuu/Debug1\n"); fprintf (e, "callfile /usr/tmp/tstuu/Call1\n"); fprintf (e, "pubdir /usr/tmp/tstuu\n"); #if HAVE_V2_CONFIG fprintf (e, "v2-files no\n"); #endif #if HAVE_BNU_CONFIG fprintf (e, "bnu-files no\n"); #endif if (zDebug != NULL) fprintf (e, "debug %s\n", zDebug); xfclose (e); e = xfopen ("/usr/tmp/tstuu/System1", "w"); fprintf (e, "# This file is ignored, to test multiple system files\n"); fprintf (e, "time never\n"); xfclose (e); e = xfopen ("/usr/tmp/tstuu/System1.2", "w"); fprintf (e, "# First test system file\n"); fprintf (e, "time Any\n"); fprintf (e, "port stdin\n"); fprintf (e, "# That was the defaults\n"); fprintf (e, "system %s\n", zsys); if (! fcall_uucico) { FILE *eprog; eprog = xfopen ("/usr/tmp/tstuu/Chat1", "w"); /* Wait for the other side to open the port and flush input. */ fprintf (eprog, "sleep 1\n"); fprintf (eprog, "echo password $1 speed $2 1>&2\n"); fprintf (eprog, "echo test1\n"); fprintf (eprog, "exit 0\n"); xfclose (eprog); (void) chmod ("/usr/tmp/tstuu/Chat1", S_IRWXU); fprintf (e, "chat-program /usr/tmp/tstuu/Chat1 \\P \\S\n"); fprintf (e, "chat word: \\P\n"); fprintf (e, "chat-fail login;\n"); fprintf (e, "call-login *\n"); fprintf (e, "call-password *\n"); } else fprintf (e, "chat \"\"\n"); fprintf (e, "call-transfer yes\n"); fprintf (e, "commands cat\n"); if (! fcall_uucico && iPercent == 0) { fprintf (e, "protocol-parameter g window 7\n"); fprintf (e, "protocol-parameter g packet-size 4096\n"); } if (zProtocols != NULL) fprintf (e, "protocol %s\n", zProtocols); xfclose (e); e = xfopen ("/usr/tmp/tstuu/Port1", "w"); fprintf (e, "port stdin\n"); fprintf (e, "type stdin\n"); fprintf (e, "pty true\n"); xfclose (e); e = xfopen ("/usr/tmp/tstuu/Call1", "w"); fprintf (e, "Call out password file\n"); fprintf (e, "%s test1 pass1\n", zsys); xfclose (e); if (! fcall_uucico) { FILE *eprog; e = xfopen ("/usr/tmp/tstuu/Config2", "w"); fprintf (e, "# Second test configuration file\n"); fprintf (e, "nodename test2\n"); fprintf (e, "spool /usr/tmp/tstuu/spool2\n"); fprintf (e, "sysfile /usr/tmp/tstuu/System2\n"); (void) remove ("/usr/tmp/tstuu/Log2"); #if ! HAVE_BNU_LOGGING fprintf (e, "logfile /usr/tmp/tstuu/Log2\n"); #else fprintf (e, "%s\n", "logfile /usr/tmp/tstuu/Log2/%s/%s"); #endif fprintf (e, "statfile /usr/tmp/tstuu/Stats2\n"); fprintf (e, "debugfile /usr/tmp/tstuu/Debug2\n"); fprintf (e, "passwdfile /usr/tmp/tstuu/Pass2\n"); fprintf (e, "pubdir /usr/tmp/tstuu\n"); #if HAVE_V2_CONFIG fprintf (e, "v2-files no\n"); #endif #if HAVE_BNU_CONFIG fprintf (e, "bnu-files no\n"); #endif if (zDebug != NULL) fprintf (e, "debug %s\n", zDebug); xfclose (e); e = xfopen ("/usr/tmp/tstuu/System2", "w"); fprintf (e, "# Second test system file\n"); fprintf (e, "system test1\n"); fprintf (e, "called-login test1\n"); fprintf (e, "called-request true\n"); if (zProtocols != NULL) fprintf (e, "protocol %s\n", zProtocols); eprog = xfopen ("/usr/tmp/tstuu/Chat2", "w"); fprintf (eprog, "echo port $1 1>&2\n"); fprintf (eprog, "exit 0\n"); xfclose (eprog); fprintf (e, "called-chat-program /bin/sh /usr/tmp/tstuu/Chat2 \\Y\n"); fprintf (e, "time Any\n"); xfclose (e); e = xfopen ("/usr/tmp/tstuu/Pass2", "w"); fprintf (e, "# Call in password file\n"); fprintf (e, "test1 pass1\n"); xfclose (e); } zuucp1 = "./uucp -I /usr/tmp/tstuu/Config1 -r"; zuux1 = "./uux -I /usr/tmp/tstuu/Config1 -r"; if (fcall_uucico) { zuucp2 = "/usr/bin/uucp -r"; zuux2 = "/usr/bin/uux -r"; } else { zuucp2 = "./uucp -I /usr/tmp/tstuu/Config2 -r"; zuux2 = "./uux -I /usr/tmp/tstuu/Config2 -r"; } /* Test transferring a file from the first system to the second. */ if (itest == 0 || itest == 1) { zfrom = "/usr/tmp/tstuu/from1"; if (fcall_uucico) zto = "/usr/spool/uucppublic/to1"; else zto = "/usr/tmp/tstuu/to1"; (void) remove (zto); umake_file (zfrom, 0); sprintf (ab, "%s %s %s!%s", zuucp1, zfrom, zsys, zto); xsystem (ab); } /* Test having the first system request a file from the second. */ if (itest == 0 || itest == 2) { if (fcall_uucico) zfrom = "/usr/spool/uucppublic/from2"; else zfrom = "/usr/tmp/tstuu/from2"; zto = "/usr/tmp/tstuu/to2"; (void) remove (zto); umake_file (zfrom, 3); sprintf (ab, "%s %s!%s %s", zuucp1, zsys, zfrom, zto); xsystem (ab); } /* Test having the second system send a file to the first. */ if (itest == 0 || itest == 3) { if (fcall_uucico) zfrom = "/usr/spool/uucppublic/from3"; else zfrom = "/usr/tmp/tstuu/from3"; zto = "/usr/tmp/tstuu/to3"; (void) remove (zto); umake_file (zfrom, 5); sprintf (ab, "%s -c \\~/from3 test1!~/to3", zuucp2); xsystem (ab); } /* Test having the second system request a file from the first. */ if (itest == 0 || itest == 4) { zfrom = "/usr/tmp/tstuu/from4"; if (fcall_uucico) zto = "/usr/spool/uucppublic/to4"; else zto = "/usr/tmp/tstuu/to4"; (void) remove (zto); umake_file (zfrom, 7); sprintf (ab, "%s test1!%s %s", zuucp2, zfrom, zto); xsystem (ab); } /* Test having the second system make an execution request. */ if (itest == 0 || itest == 5) { zfrom = "/usr/tmp/tstuu/from5"; if (fcall_uucico) zto = "/usr/spool/uucppublic/to5"; else zto = "/usr/tmp/tstuu/to5"; (void) remove (zto); umake_file (zfrom, 11); sprintf (ab, "%s test1!cat '<%s' '>%s'", zuux2, zfrom, zto); xsystem (ab); } /* Test having the first system request a wildcard. */ if (itest == 0 || itest == 6) { const char *zfrom1, *zfrom2; if (fcall_uucico) { zfrom = "/usr/spool/uucppublic/to6\\*"; zfrom1 = "/usr/spool/uucppublic/to6.1"; zfrom2 = "/usr/spool/uucppublic/to6.2"; } else { zfrom = "/usr/tmp/tstuu/spool2/to6\\*"; zfrom1 = "/usr/tmp/tstuu/spool2/to6.1"; zfrom2 = "/usr/tmp/tstuu/spool2/to6.2"; } umake_file (zfrom1, 100); umake_file (zfrom2, 101); (void) remove ("/usr/tmp/tstuu/to6.1"); (void) remove ("/usr/tmp/tstuu/to6.2"); sprintf (ab, "%s %s!%s /usr/tmp/tstuu", zuucp1, zsys, zfrom); xsystem (ab); } /* Test having the second system request a wildcard. */ if (itest == 0 || itest == 7) { const char *zto1, *zto2; if (fcall_uucico) { zto = "/usr/spool/uucppublic"; zto1 = "/usr/spool/uucppublic/to7.1"; zto2 = "/usr/spool/uucppublic/to7.2"; } else { zto = "/usr/tmp/tstuu"; zto1 = "/usr/tmp/tstuu/to7.1"; zto2 = "/usr/tmp/tstuu/to7.2"; } umake_file ("/usr/tmp/tstuu/spool1/to7.1", 150); umake_file ("/usr/tmp/tstuu/spool1/to7.2", 155); (void) remove (zto1); (void) remove (zto2); sprintf (ab, "%s test1!/usr/tmp/tstuu/spool1/to7.\\* %s", zuucp2, zto); xsystem (ab); } } /* Try to make sure the file transfers were successful. */ static void ucheck_test (itest, fcall_uucico) int itest; boolean fcall_uucico; { if (itest == 0 || itest == 1) { if (fcall_uucico) ucheck_file ("/usr/spool/uucppublic/to1", "test 1", 0); else ucheck_file ("/usr/tmp/tstuu/to1", "test 1", 0); } if (itest == 0 || itest == 2) ucheck_file ("/usr/tmp/tstuu/to2", "test 2", 3); if (itest == 0 || itest == 3) ucheck_file ("/usr/tmp/tstuu/to3", "test 3", 5); if (itest == 0 || itest == 4) { if (fcall_uucico) ucheck_file ("/usr/spool/uucppublic/to4", "test 4", 7); else ucheck_file ("/usr/tmp/tstuu/to4", "test 4", 7); } if (itest == 0 || itest == 6) { ucheck_file ("/usr/tmp/tstuu/to6.1", "test 6.1", 100); ucheck_file ("/usr/tmp/tstuu/to6.2", "test 6.2", 101); } if (itest == 0 || itest == 7) { const char *zto1, *zto2; if (fcall_uucico) { zto1 = "/usr/spool/uucppublic/to7.1"; zto2 = "/usr/spool/uucppublic/to7.2"; } else { zto1 = "/usr/tmp/tstuu/to7.1"; zto2 = "/usr/tmp/tstuu/to7.2"; } ucheck_file (zto1, "test 7.1", 150); ucheck_file (zto2, "test 7.2", 155); } } /* A debugging routine used when displaying buffers. */ static int cpshow (z, ichar) char *z; int ichar; { if (isprint (BUCHAR (ichar)) && ichar != '\"') { *z = (char) ichar; return 1; } *z++ = '\\'; switch (ichar) { case '\n': *z = 'n'; return 2; case '\r': *z = 'r'; return 2; case '\"': *z = '\"'; return 2; default: sprintf (z, "%03o", (unsigned int)(ichar & 0xff)); return strlen (z) + 1; } } /* Transfer data from one pseudo-terminal to the other. */ static void utransfer (ofrom, oto, otoslave, pc) int ofrom; int oto; int otoslave; int *pc; { int cread; #ifdef FIONREAD char abbuf[10000]; #else char abbuf[80]; #endif char *zwrite; cread = read (ofrom, abbuf, sizeof abbuf); if (cread < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) cread = 0; else { perror ("read"); uchild (SIGCHLD); } } if (zDebug != NULL) { char abshow[325]; char *zshow; int i; zshow = abshow; for (i = 0; i < cread && i < 80; i++) zshow += cpshow (zshow, abbuf[i]); if (i < cread) { *zshow++ = '.'; *zshow++ = '.'; *zshow++ = '.'; } *zshow = '\0'; fprintf (stderr, "Writing to %d: %d \"%s\"\n", oto, cread, abshow); fflush (stderr); } if (iPercent > 0) { int i; int c; c = 0; for (i = 0; i < cread; i++) { if (rand () % 100 < iPercent) { ++abbuf[i]; ++c; } } if (zDebug != NULL && c > 0) fprintf (stderr, "Clobbered %d bytes\n", c); } zwrite = abbuf; while (cread > 0) { long cunread; int cdo; int cwrote; #ifdef FIONREAD if (ioctl (otoslave, FIONREAD, &cunread) < 0) { perror ("FIONREAD"); uchild (SIGCHLD); } if (zDebug != NULL) fprintf (stderr, "%ld unread\n", cunread); #else /* ! FIONREAD */ cunread = 0; #endif /* ! FIONREAD */ cdo = cread; if (256 - cunread < cdo) { cdo = 256 - cunread; if (cdo == 0) continue; } cwrote = write (oto, zwrite, cdo); if (cwrote < 0) { perror ("write"); uchild (SIGCHLD); } cread -= cwrote; zwrite += cwrote; *pc += cwrote; } } /* A version of the system command that checks for errors. */ static void xsystem (zcmd) const char *zcmd; { int istat; istat = system ((char *) zcmd); if (istat != 0) { fprintf (stderr, "Command failed with status %d\n", istat); fprintf (stderr, "%s\n", zcmd); exit (EXIT_FAILURE); } } /* Pick one of two file descriptors which is ready for reading, or return in five seconds. If the argument is ready for reading, leave it alone; otherwise set it to -1. */ static void uchoose (po1, po2) int *po1; int *po2; { #if HAVE_SELECT int iread; struct timeval stime; iread = (1 << *po1) | (1 << *po2); stime.tv_sec = 5; stime.tv_usec = 0; if (select ((*po1 > *po2 ? *po1 : *po2) + 1, &iread, (int *) NULL, (int *) NULL, &stime) < 0) { perror ("select"); uchild (SIGCHLD); } if ((iread & (1 << *po1)) == 0) *po1 = -1; if ((iread & (1 << *po2)) == 0) *po2 = -1; #else /* ! HAVE_SELECT */ #if HAVE_POLL struct pollfd as[2]; as[0].fd = *po1; as[0].events = POLLIN; as[1].fd = *po2; as[1].events = POLLIN; if (poll (as, 2, 5 * 1000) < 0) { perror ("poll"); uchild (SIGCHLD); } if ((as[0].revents & POLLIN) == 0) *po1 = -1; if ((as[1].revents & POLLIN) == 0) *po2 = -1; #endif /* HAVE_POLL */ #endif /* ! HAVE_SELECT */ } /* Check whether a file descriptor can be written to. */ static boolean fwritable (o) int o; { #if HAVE_SELECT int iwrite; struct timeval stime; int cfds; iwrite = 1 << o; stime.tv_sec = 0; stime.tv_usec = 0; cfds = select (o + 1, (int *) NULL, &iwrite, (int *) NULL, &stime); if (cfds < 0) { perror ("select"); uchild (SIGCHLD); } return cfds > 0; #else /* ! HAVE_SELECT */ #if HAVE_POLL struct pollfd s; int cfds; s.fd = o; s.events = POLLOUT; cfds = poll (&s, 1, 0); if (cfds < 0) { perror ("poll"); uchild (SIGCHLD); } return cfds > 0; #endif /* HAVE_POLL */ #endif /* ! HAVE_SELECT */ } /* We don't want to link in util.c, since that would bring in the log file stuff. Instead, we have local copies of functions that may be needed by getopt.c. This should be done in a cleaner way. */ #if ! HAVE_MEMCPY && ! HAVE_BCOPY /* Copy one block of memory to another. */ pointer memcpy (ptoarg, pfromarg, c) pointer ptoarg; constpointer pfromarg; int c; { char *pto = (char *) ptoarg; const char *pfrom = (const char *) pfromarg; while (c-- != 0) *pto++ = *pfrom++; return ptoarg; } #endif /* ! HAVE_MEMCPY && ! HAVE_BCOPY */ #if ! HAVE_STRCHR && ! HAVE_INDEX /* Look for a character in a string. This is supposed to work for a null byte, although we never actually call it with one. */ char * strchr (z, b) const char *z; int b; { b = (char) b; while (*z != b) if (*z++ == '\0') return NULL; return (char *) z; } #endif /* ! HAVE_STRCHR && ! HAVE_INDEX */