/* tstuu.c Test the uucp package on a UNIX system. Copyright (C) 1991, 1992, 1993, 1994, 1995 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. The author of the program may be contacted at ian@airs.com or c/o Cygnus Support, 48 Grove Street, Somerville, MA 02144. */ #include "uucp.h" #if USE_RCS_ID const char tstuu_rcsid[] = "$Id: tstuu.c,v 1.5 2003/01/06 20:30:43 wiz Exp $"; #endif #include "sysdep.h" #include "system.h" #include "getopt.h" #include #include #include #if HAVE_SYS_TIMES_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_SELECT #if HAVE_SYS_TIME_H #include #endif #if HAVE_SYS_SELECT_H #include #endif #endif #if HAVE_POLL #if HAVE_STROPTS_H #include #endif #if HAVE_POLL_H #include #endif #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 #if ! HAVE_SYS_TIME_H || ! HAVE_SELECT || TIME_WITH_SYS_TIME #include #endif #endif #if HAVE_SYS_WAIT_H #include #endif #if HAVE_UNION_WAIT typedef union wait wait_status; #else typedef int wait_status; #endif #if HAVE_STREAMS_PTYS #include extern char *ptsname (); #endif /* 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 /* Get definitions for EAGAIN, EWOULDBLOCK and ENODATA. */ #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) */ #ifndef ENODATA #define ENODATA EAGAIN #endif /* 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 #if 1 #define ZUUCICO_CMD "login uucp" #define UUCICO_EXECL "/bin/login", "login", "uucp" #else #define ZUUCICO_CMD "su - nuucp" #define UUCICO_EXECL "/bin/su", "su", "-", "nuucp" #endif #if ! HAVE_SELECT && ! HAVE_POLL #error You need select or poll #endif #if ! HAVE_REMOVE #undef remove #define remove unlink #endif /* Buffer chain to hold data read from a uucico. */ #define BUFCHARS (512) struct sbuf { struct sbuf *qnext; int cstart; int cend; char ab[BUFCHARS]; }; /* Local functions. */ static void umake_file P((const char *zfile, int cextra)); static void uprepare_test P((boolean fmake, 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 RETSIGTYPE uchild P((int isig)); static int cpshow P((char *z, int bchar)); static void uchoose P((int *po1, int *po2)); static long cread P((int o, struct sbuf **)); static boolean fsend P((int o, int oslave, struct sbuf **)); static boolean fwritable P((int o)); static void xsystem P((const char *zcmd)); static FILE *xfopen P((const char *zname, const char *zmode)); 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 *zsys; boolean fmake = TRUE; int omaster1, oslave1, omaster2, oslave2; char abpty1[sizeof "/dev/ptyp0"]; char abpty2[sizeof "/dev/ptyp0"]; struct sbuf *qbuf1, *qbuf2; #if ! HAVE_TAYLOR_CONFIG fprintf (stderr, "%s: only works when compiled with HAVE_TAYLOR_CONFIG\n", argv[0]); exit (1); #endif zcmd1 = NULL; zcmd2 = NULL; zsys = "test2"; while ((iopt = getopt (argc, argv, "c:np:s:t:ux:1:2:")) != EOF) { switch (iopt) { case 'c': zProtocols = optarg; break; case 'n': fmake = FALSE; break; case 'p': iPercent = (int) strtol (optarg, (char **) NULL, 10); srand ((unsigned int) ixsysdep_time ((long *) NULL)); break; case 's': zsys = optarg; break; case 't': iTest = (int) strtol (optarg, (char **) NULL, 10); 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 %s, copyright (C) 1991, 92, 93, 94, 1995 Ian Lance Taylor\n", VERSION); fprintf (stderr, "Usage: tstuu [-xn] [-t #] [-u] [-1 cmd] [-2 cmd]\n"); exit (EXIT_FAILURE); } } if (fCall_uucico && zcmd2 == NULL) zcmd2 = ZUUCICO_CMD; uprepare_test (fmake, 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; #if ! HAVE_STREAMS_PTYS { char *zptyname; const char *zpty; 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; } } #else /* HAVE_STREAMS_PTYS */ { int ipty; for (ipty = 0; ipty < 2; ipty++) { int om, os; FILE *e; char *znam; struct termio stio; om = open ((char *) "/dev/ptmx", O_RDWR); if (om < 0) break; znam = ptsname (om); if (znam == NULL) break; if (unlockpt (om) != 0 || grantpt (om) != 0) break; os = open (znam, O_RDWR); if (os < 0) { (void) close (om); om = -1; break; } if (ioctl (os, I_PUSH, "ptem") < 0 || ioctl(os, I_PUSH, "ldterm") < 0) { perror ("ioctl"); exit (EXIT_FAILURE); } /* Can this really be right? */ memset (&stio, 0, sizeof (stio)); stio.c_cflag = B9600 | CS8 | CREAD | HUPCL; if (ioctl(os, TCSETA, &stio) < 0) { perror ("TCSETA"); exit (EXIT_FAILURE); } if (omaster1 == -1) { strcpy (abpty1, znam); omaster1 = om; oslave1 = os; e = fopen ("/usr/tmp/tstuu/pty1", "w"); if (e == NULL) { perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "%s", znam + 5); if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } } else { strcpy (abpty2, znam); omaster2 = om; oslave2 = os; e = fopen ("/usr/tmp/tstuu/pty2", "w"); if (e == NULL) { perror ("fopen"); exit (EXIT_FAILURE); } fprintf (e, "%s", znam + 5); if (fclose (e) != 0) { perror ("fclose"); exit (EXIT_FAILURE); } } } } #endif /* HAVE_STREAMS_PTYS */ 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"); /* This is said to improve the tests on Linux. */ sleep (3); 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); perror ("execl failed"); 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"); /* This is said to improve the tests on Linux. */ sleep (5); if (zDebug != NULL) fprintf (stderr, "About to exec second process\n"); if (fCall_uucico) { (void) execl (UUCICO_EXECL, (const char *) NULL); perror ("execl failed"); 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); perror ("execl failed"); exit (EXIT_FAILURE); } } signal (SIGCHLD, uchild); if (fcntl (omaster1, F_SETFL, O_NDELAY | O_NONBLOCK) < 0 && errno == EINVAL) (void) fcntl (omaster1, F_SETFL, O_NONBLOCK); if (fcntl (omaster2, F_SETFL, O_NDELAY | O_NONBLOCK) < 0 && errno == EINVAL) (void) fcntl (omaster2, F_SETFL, O_NONBLOCK); qbuf1 = NULL; qbuf2 = NULL; while (TRUE) { int o1, o2; boolean fcont; 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) cFrom1 += cread (omaster1, &qbuf1); if (o2 != -1) cFrom2 += cread (omaster2, &qbuf2); do { fcont = FALSE; if (qbuf1 != NULL && fwritable (omaster2) && fsend (omaster2, oslave2, &qbuf1)) fcont = TRUE; if (qbuf2 != NULL && fwritable (omaster1) && fsend (omaster1, oslave1, &qbuf2)) fcont = TRUE; if (! fcont && (qbuf1 != NULL || qbuf2 != NULL)) { long cgot1, cgot2; cgot1 = cread (omaster1, &qbuf1); cFrom1 += cgot1; cgot2 = cread (omaster2, &qbuf2); cFrom2 += cgot2; fcont = TRUE; } } while (fcont); } /*NOTREACHED*/ } /* When a child dies, kill them both. */ static RETSIGTYPE uchild (isig) int isig; { struct tms sbase, s1, s2; signal (SIGCHLD, SIG_DFL); /* Give the processes a chance to die on their own. */ sleep (2); (void) kill (iPid1, SIGTERM); (void) kill (iPid2, SIGTERM); (void) times (&sbase); #if HAVE_WAITPID (void) waitpid (iPid1, (pointer) NULL, 0); #else /* ! HAVE_WAITPID */ #if HAVE_WAIT4 (void) wait4 (iPid1, (pointer) 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, (pointer) 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 (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 (fmake, itest, fcall_uucico, zsys) boolean fmake; 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 writable 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); } if (fmake) { 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, "lockdir /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_HDB_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_HDB_CONFIG fprintf (e, "hdb-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 2\n"); fprintf (eprog, "echo password $1 speed $2 1>&2\n"); fprintf (eprog, "echo test1\n"); fprintf (eprog, "exit 0\n"); xfclose (eprog); if (chmod ("/usr/tmp/tstuu/Chat1", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { perror ("chmod (/usr/tmp/tstuu/Chat1)"); exit (EXIT_FAILURE); } 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"); fprintf (e, "protocol-parameter j avoid \\377\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"); xfclose (e); e = xfopen ("/usr/tmp/tstuu/Call1", "w"); fprintf (e, "Call out password file\n"); fprintf (e, "%s test1 pass\\s1\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, "lockdir /usr/tmp/tstuu/spool2\n"); fprintf (e, "sysfile /usr/tmp/tstuu/System2\n"); (void) remove ("/usr/tmp/tstuu/Log2"); #if ! HAVE_HDB_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_HDB_CONFIG fprintf (e, "hdb-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, "request true\n"); fprintf (e, "commands cat\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); if (chmod ("/usr/tmp/tstuu/Chat2", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0) { perror ("chmod (/usr/tmp/tstuu/Chat2"); exit (EXIT_FAILURE); } 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 pass\\s1\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); } /* Test an E command. This runs cat, discarding the output. */ if ((itest == 0 || itest == 8) && ! fcall_uucico) { umake_file ("/usr/tmp/tstuu/from8", 30); sprintf (ab, "%s - test2!cat < /usr/tmp/tstuu/from8", zuux1); 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; } } /* 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, (pointer) &iread, (pointer) NULL, (pointer) 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 */ } /* Read some data from a file descriptor. This keeps reading until one of the reads gets no data. */ static long cread (o, pqbuf) int o; struct sbuf **pqbuf; { long ctotal; while (*pqbuf != NULL && (*pqbuf)->qnext != NULL) pqbuf = &(*pqbuf)->qnext; ctotal = 0; while (TRUE) { int cgot; if (*pqbuf != NULL && (*pqbuf)->cend >= sizeof (*pqbuf)->ab) pqbuf = &(*pqbuf)->qnext; if (*pqbuf == NULL) { *pqbuf = (struct sbuf *) malloc (sizeof (struct sbuf)); if (*pqbuf == NULL) { fprintf (stderr, "Out of memory\n"); uchild (SIGCHLD); } (*pqbuf)->qnext = NULL; (*pqbuf)->cstart = 0; (*pqbuf)->cend = 0; } cgot = read (o, (*pqbuf)->ab + (*pqbuf)->cend, (sizeof (*pqbuf)->ab) - (*pqbuf)->cend); if (cgot < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA) cgot = 0; else { perror ("read"); uchild (SIGCHLD); } } if (cgot == 0) return ctotal; ctotal += cgot; if (zDebug != NULL) { char abshow[325]; char *zfrom; char *zshow; int i; zfrom = (*pqbuf)->ab + (*pqbuf)->cend; zshow = abshow; for (i = 0; i < cgot && i < 80; i++, zfrom++) zshow += cpshow (zshow, *zfrom); if (i < cgot) { *zshow++ = '.'; *zshow++ = '.'; *zshow++ = '.'; } *zshow = '\0'; fprintf (stderr, "Read from %d: %d \"%s\"\n", o, cgot, abshow); fflush (stderr); } if (iPercent > 0) { int i; int c; c = 0; for (i = 0; i < cgot; i++) { if (rand () % 1000 < iPercent) { ++(*pqbuf)->ab[(*pqbuf)->cend + i]; ++c; } } if (zDebug != NULL && c > 0) fprintf (stderr, "Clobbered %d bytes\n", c); } (*pqbuf)->cend += cgot; if (ctotal > 256) return ctotal; } } /* Write data to a file descriptor until one of the writes gets no data. */ static boolean fsend (o, oslave, pqbuf) int o; int oslave; struct sbuf **pqbuf; { long ctotal; ctotal = 0; while (*pqbuf != NULL) { int cwrite, cwrote; if ((*pqbuf)->cstart >= (*pqbuf)->cend) { struct sbuf *qfree; qfree = *pqbuf; *pqbuf = (*pqbuf)->qnext; free ((pointer) qfree); continue; } #ifdef FIONREAD { long cunread; if (ioctl (oslave, FIONREAD, &cunread) < 0) { perror ("FIONREAD"); uchild (SIGCHLD); } if (zDebug != NULL) fprintf (stderr, "%ld unread\n", cunread); cwrite = 256 - cunread; if (cwrite <= 0) break; } #else /* ! FIONREAD */ if (! fwritable (o)) break; cwrite = 1; #endif /* ! FIONREAD */ if (cwrite > (*pqbuf)->cend - (*pqbuf)->cstart) cwrite = (*pqbuf)->cend - (*pqbuf)->cstart; cwrote = write (o, (*pqbuf)->ab + (*pqbuf)->cstart, cwrite); if (cwrote < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK || errno == ENODATA) cwrote = 0; else { perror ("write"); uchild (SIGCHLD); } } if (cwrote == 0) break; ctotal += cwrote; (*pqbuf)->cstart += cwrote; } if (zDebug != NULL && ctotal > 0) fprintf (stderr, "Wrote %ld to %d\n", ctotal, o); return ctotal > 0; } /* 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, (pointer) NULL, (pointer) &iwrite, (pointer) 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 */ } /* 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); } }