[436] in linux-net channel archive
Networking(?) problems with CPIO/RMT
daemon@ATHENA.MIT.EDU (Andreas Haumer)
Sat Jun 10 19:34:26 1995
Date: Sun, 11 Jun 95 00:37 MET DST
From: andreas@vlsivie.tuwien.ac.at (Andreas Haumer)
To: linux-net@vger.rutgers.edu
Cc: andreas@vlsivie.tuwien.ac.at
Hi!
Last weekend I reported a problem to comp.os.linux.networking, regarding
a hangup problem with "cpio" and "rmt".
Thomas Koenig then made a follow-up to this list, and there was also a
short answer from Mark Eichin regarding his solution to the "rdist" problem.
But alas, I still have no solution to the "cpio" hangup. In the past week,
I wrote a small test-program and did some tests (and transferred several
giga-bytes through my LAN!) but I was NOT able to reproduce the problem with
my test-program. Instead, I still get hangups with cpio/rmt! (Have you ever
encountered the situation where you want to, but can't get your program to
fail? ;-)
I also NEVER had the situation where a write(2) to a pipe or socket returned
an indication that only _some_ of the requested data got written (as Mark
reported it was the reason for the "rdist" problem). I got this situation
with read(2), though!
Here is what I did:
First I tried to find out, what "cpio" does when it has to read from a
remote tape.
I learned, that "cpio" executes "rsh" in order to start the "rmt" process
on the remote system and does the client/server communication through
a pair of pipes between itself and the "rsh" process.
The "rsh" process uses standard socket I/O (non-blocking!) to communicate
with the "rmt" process on the remote system (where "rmt" doesn't know
anything about this, because it just uses stdin/stdout and gets its
filedescriptors already connected to the corresponding sockets by "inetd").
I then wrote a simple client/server application to check what's going on.
I tried to write it as close as possible to the cpio/rsh/rmt functions in
question (I have included the sources at the end of this mail, in case
someone wants to try it).
The client just sends a request to the server, indicating it wants to
read some random data, and the server then sends the number of bytes
requested.
Using command-line options, it can be set into several different operation
modes, see the sources for a short description.
It supports both the "pipe-through-rsh" and the "direct socket" I/O methods.
But as I said, I could not bring the test application to hang, even when
transferring more than 100MB in 5k blocks! On the other hand, "cpio" hangs
after a few MB!
Conclusion: none, so far! I'm just running out of ideas.
I'm not even sure anymore if it's a network-related problem! But on the other
hand, where do these 6 bytes hanging around in the send-queue come from? And
why does the corresponding "shell" socket on the remote system then just
remain in state "FIN_WAIT2"?
Next thing I want to check is if it could be some kind of timing problem in
the SCSI tape and/or network software because my test program doesn't address
this yet.
Any ideas and hints are welcome!
- andreas
Source code of test program following:
---------8<------------8<------------8<----------8<-----------8<----------
/*
* client.c - Linux network communication testprogram - client
*
* written by Andreas Haumer (andreas@vlsivie.tuwien.ac.at), June 1995
*
*
* This program communicates with a server program on a remote host. Its only
* purpose is to continuously transmit send-requests to the server and then to
* receive the requested data.
*
* compile-command: cc -O2 -m486 -Wall -o client client.c
*
* There are several command-line options:
* -h host ... name of the server-host
* -n num .... Number of bytes the server has to transmit on each request.
* -m max .... max. number of requests the client issues.
* -B ........ do blocking I/O (wait for each read/write system-call to
* finish).
* -S ........ call select(2) before each read/write system-call to
* check if the I/O operation was allowed.
* -P ........ do pipe I/O via rsh(1), otherwise do direct client/server
* socket I/O.
* You have to start the server-process on the remote host
* with "server" first when using direct socket I/O!
* -v ........ tell number of bytes received.
*/
#include <stdio.h>
#include <stdlib.h> /* malloc, atoi */
#include <unistd.h> /* sysconf */
#include <string.h> /* memset */
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h> /* socket stuff */
#include <sys/ioctl.h>
#include <netinet/in.h> /* socket stuff */
#include <netdb.h> /* gethostbyname */
#define PORT 0x1234 /* unprivileged port number */
#define SERVERPROG "/usr/src/nettest/server"
#define RSHPROG "/usr/bin/rsh"
/* program-options */
int verbose;
int do_select;
int pipeio;
int blocking;
int maxreq;
void usage (char *programname)
{
printf ("usage: %s [-h host] [-n num] [-m max] [-B] [-S] [-P] [-v]\n",
programname);
printf ("\t-h host ... name of server host (default: localhost)\n");
printf ("\t-n num .... bytes to transmit on each request (default: 5120)\n");
printf ("\t-m max .... max. number of requests (default: 32768)\n");
printf ("\t-B ........ do blocking I/O (default: non-blocking I/O)\n");
printf ("\t-S ........ call select before I/O ops (default: don't)\n");
printf ("\t-P ........ do pipe I/O (default: do direct socket I/O)\n");
printf ("\t-v ........ be verbose (default: be quiet)\n");
}
int doit (int in_fd, int out_fd, int n)
{
char wbuf[64], *rbuf;
int req, i, wc, cc;
unsigned long rcnt = 0L;
int tsize = (int)sysconf (_SC_OPEN_MAX);
fd_set r_set, w_set;
sprintf (wbuf, "S%d\n", n);
wc = strlen (wbuf);
if ((rbuf = (char *)malloc (n)) == NULL) {
perror ("malloc");
return (-1);
}
for (req=0; req < maxreq; req++) {
if (do_select) {
/*
* Check and wait until process is able to write to file <fd>
*/
FD_ZERO (&w_set);
FD_SET (out_fd, &w_set);
if (select (tsize, NULL, &w_set, NULL, NULL) == -1) {
perror ("select");
return (-1);
}
if (!FD_ISSET (out_fd, &w_set))
continue; /* not the file we want to read to */
}
/*
* OK, we're allowed to write our command, let's do it.
* Report an error if we can't write all data we want to.
*/
if (write (out_fd, wbuf, wc) != wc) {
perror ("write"); /* write problem? */
return (-1);
}
/*
* We exactly expect <n> bytes, but data could be splitted into several
* blocks, so read until we got all <n> bytes.
*/
for (cc=i=0; i<n; i += cc) {
if (do_select) {
/*
* Now check and wait until there is something to read on <fd>
* (the answer from our server process!)
*/
FD_ZERO (&r_set);
FD_SET (in_fd, &r_set);
if (select (tsize, &r_set, NULL, NULL, NULL) == -1) {
perror ("select");
return (-1);
}
if (!FD_ISSET (in_fd, &r_set))
continue; /* not the file we want to read from */
}
/*
* OK, there is some data to read from <fd>. Get it!
* In case of non-blocking I/O, read(2) can return on a "there is no
* data available yet" condition (<errno> is set to EAGAIN). In that case
* we just repeat the operation.
*/
if ((cc = read (in_fd, &rbuf[i], n-i)) < 0) {
if (errno == EAGAIN)
cc = 0; /* no error, just try it again */
else {
perror ("read");
return (-1);
}
}
} /* for (i) */
rcnt += i; /* count number of bytes read */
if (verbose) {
printf ("got %ld chars\r", rcnt);
fflush (stdout);
}
} /* for (req) */
if (verbose)
printf ("\nok.\n");
return (0);
} /* doit */
int main (int argc, char *argv[])
{
extern int getopt();
extern char *optarg;
char c, *hostname;
int in_fd, out_fd, n, sd, one = 1;
struct sockaddr_in p_in;
struct hostent *hp;
hostname = "localhost";
n = 5120;
blocking = 0;
verbose = 0;
pipeio = 0;
do_select = 0;
maxreq = 32768;
while ((c = getopt (argc, argv, "h:m:n:vBSP")) != EOF)
switch (c) {
case 'h':
hostname = optarg; /* name of server host */
break;
case 'm':
maxreq = atoi (optarg); /* max. number of requests */
break;
case 'n':
n = atoi (optarg); /* number of bytes to transmit */
break;
case 'v':
verbose = 1; /* be verbose */
break;
case 'B':
blocking = 1; /* do blocking I/O */
break;
case 'P':
pipeio = 1; /* do pipe I/O (use rsh(1)) */
break;
case 'S':
do_select = 1; /* call select(2) before read/write */
break;
default:
usage (argv[0]);
exit (-1);
} /* switch */
if (!pipeio) { /* use direct socket communication */
if ((hp = gethostbyname (hostname)) == NULL) {
perror ("gethostbyname");
exit (-1);
}
memset (&p_in, 0, sizeof (p_in));
p_in.sin_family = AF_INET;
p_in.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr;
p_in.sin_port = htons (PORT);
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
perror ("socket");
exit (-1);
}
if (connect (sd, (struct sockaddr *)&p_in, sizeof (p_in)) == -1) {
perror ("connect");
exit (-1);
}
in_fd = sd;
out_fd = sd;
}
else { /* do it the cpio(1) way */
int in_pfd[2], out_pfd[2];
/*
* create pipes to child process ("rsh")
*/
if ((pipe (in_pfd) == -1) || (pipe (out_pfd) == -1)) {
perror ("pipe");
exit (-1);
}
switch (fork ()) {
case -1:
perror ("fork");
exit (-1);
case 0: /* child */
close (0);
dup (in_pfd[0]); /* stdin */
close (in_pfd[0]);
close (in_pfd[1]);
close (1);
dup (out_pfd[1]); /* stdout */
close (out_pfd[0]);
close (out_pfd[1]);
execl (RSHPROG, "rsh", hostname,
SERVERPROG, "-P", (char *)0);
perror ("execl");
exit (-1);
default: /* parent */
close (in_pfd[0]);
close (out_pfd[1]);
in_fd = out_pfd[0];
out_fd = in_pfd[1];
} /* switch */
} /* else */
if (!blocking) {
if (ioctl (in_fd, FIONBIO, &one) == -1) {
perror ("ioctl");
exit (-1);
}
if (ioctl (out_fd, FIONBIO, &one) == -1) {
perror ("ioctl");
exit (-1);
}
}
exit (doit (in_fd, out_fd, n));
} /* main */
---------8<------------8<------------8<----------8<-----------8<----------
/*
* server.c - Linux network communication testprogram - server
*
* written by Andreas Haumer (andreas@vlsivie.tuwien.ac.at), June 1995
*
* compile-command: cc -O2 -m486 -Wall -o server server.c
*
* There are several command-line options:
* -P ........ do pipe I/O via rsh(1), otherwise do direct client/server
* socket I/O.
* -v ........ tell number of bytes sent.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#define PORT 0x1234
#define SSIZE 64
int verbose;
int pipeio;
void getstring (int fd, char *bp)
{
int i;
char *cp = bp;
for (i = 0; i < SSIZE; i++) {
if (read (fd, cp + i, 1) != 1)
exit (0);
if (cp[i] == '\n')
break;
}
cp[i] = '\0';
}
int doit (int in_fd, int out_fd)
{
char count[SSIZE];
char cmd;
char *buf;
int i, n, m;
unsigned long wcnt = 0L;
while (read (in_fd, &cmd, 1) == 1) {
switch (cmd) {
case 'X':
return (0);
case 'S': /* send */
getstring (in_fd, count);
n = atoi (count);
if ((buf = (char *)calloc (n, sizeof (char))) == NULL) {
perror ("calloc");
return (-1);
}
/*
* create buffer of random data
*/
for (i=0; i<n; i++)
buf[i] = (char)(rand() % 256);
if ((m = write (out_fd, buf, n)) != n) {
perror ("write");
return (-1);
}
wcnt += m; /* count number of bytes written */
if (verbose) {
printf ("wrote %ld chars\r", wcnt);
fflush (stdout);
}
free (buf);
break;
default:
fprintf (stderr, "Unknown command %c\n", cmd);
return (-1);
}
} /* while */
return (0);
}
int main (int argc, char *argv[])
{
extern int getopt();
extern char *optarg;
char c;
int sd, act_sd, in_fd, out_fd;
int addrlen;
struct sockaddr_in s_in, p_in;
srand (time (NULL));
pipeio = 0;
verbose = 0;
while ((c = getopt (argc, argv, "Pv")) != EOF)
switch (c) {
case 'P':
pipeio = 1;
break;
case 'v':
verbose = 1;
break;
}
if (pipeio) {
in_fd = 0;
out_fd = 1;
}
else {
if ((sd = socket (AF_INET, SOCK_STREAM, 0)) == -1) {
perror ("socket");
exit (-1);
}
memset (&s_in, 0, sizeof (s_in));
s_in.sin_family = AF_INET;
s_in.sin_addr.s_addr = INADDR_ANY;
s_in.sin_port = htons (PORT);
if (bind (sd, (struct sockaddr *)&s_in, sizeof (s_in)) == -1) {
perror ("bind");
exit (-1);
}
if (listen (sd, 5) == -1) {
perror ("listen");
exit (-1);
}
if ((act_sd = accept (sd, (struct sockaddr *)&p_in, &addrlen)) == -1) {
perror ("accept");
exit (-1);
}
in_fd = act_sd;
out_fd = act_sd;
}
exit (doit (in_fd, out_fd));
}
---------8<------------8<------------8<----------8<-----------8<----------
--
In your head, in your head
Zombie
What's in your head, in your head?
Zombie!
"Zombie", The Cranberries
Gewidmet allen Faschisten und Bombenlegern - aus gegebenen Anlass.
----------------------+------------------------------+-------------------------
andreas haumer | andreas@vlsivie.tuwien.ac.at |
buchengasse 67/8 | tel: +43.1.6001508 (ISDN) |
a-1100 vienna | +43.664.3004449 (GSM) | god is real -
austria | fax: +43.1.6001084 | unless declared integer