views:

366

answers:

4

This code is based on splice-fromnet.c and splice-cp.c to splice from a socket to a pipe and from the pipe to a file but for some reason the first call to splice never returns.

static size_t splice_from_net_to_file(int infd, int outfd)
{
    int p[2];
    size_t total = 0;

    if (pipe(p) == -1)
        return error("pipe");
    while (1) {
        int ret;
        ret = ssplice(infd, NULL, p[1], NULL, splice_size, 0);

        if (ret < 0) {
         close(p[0]);
         close(p[1]);
         return error("splice in pipe");
        }
        else if (!ret)
         break;
        while (ret > 0) {
         int written = ssplice(p[0], NULL, outfd, NULL, ret, 0);
         if (written <= 0) {
          close(p[0]);
          close(p[1]);
          return error("splice out pipe");
         }
         ret -= written;
         total += written;
        }  
    }
    close(p[0]);
    close(p[1]);
    return total;
}

I have tested this on linux 2.6.30.

+1  A: 

Is it possible that you have not started listener on other side of pipe?

Dewfy
It wasnt that. I think splice_size was too big...
A: 

Where is splice_size being initialized?

Sean A.O. Harney
from splice-fromnet.cstatic unsigned int splice_size = SPLICE_SIZE;#define SPLICE_SIZE (64*1024)
Does the unmodified splice-fromnet work properly on your system? And the code based on it is broken only?
Sean A.O. Harney
The unmodified splice-fromnet work ok on my system. And yes.
A: 

I think this blocks because there is no reader on the pipe at the time of the write. You should be able to resolve this by using a fork().

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

static size_t splice_from_net_to_file(int infd, int outfd)
{
    int p[2];
    pid_t fpid;
    size_t total = 0;

    if (pipe(p) == -1)
        return error("pipe");

    fpid = fork();
    if (fpid == -1)
        return error("fork");

    if (!fpid) /* child */
    {
        int ret;
        close(p[0]); /* don't need reading end */
        while ((ret = ssplice(infd, NULL, p[1], NULL, splice_size, 0)) > 0)
            ;
        close(p[1]);
        if (ret < 0 )
        {
            /* error("splice in pipe") */
            exit(-1);
        }
        exit(0);
    }
    else
    {
        /* parent */
        int ret;
        close(p[1]); /* no need for writing end */
        while ((ret = ssplice(p[0], NULL, outfd, NULL, splice_size, 0)) > 0)
            total += ret;
        close(p[0]);
        waitpid(fpid, NULL, 0);

        if (ret < 0)
        {
            return error("splice out pipe");
        }
    }

    return total;
}
Hasturkun
A: 

I think I just encountered this - it's due to a kernel bug that was fixed in 2.6.32 - see http://permalink.gmane.org/gmane.linux.network/138828 for details.

There is a workaround I found at http://git.samba.org/?p=samba.git;a=commitdiff;h=e1459a237948c2c9b7da94e4ba0acc79b1cd8eca - change the first splice() call from

ret = ssplice(infd, NULL, p[1], NULL, splice_size, 0);

to

ret = ssplice(infd, NULL, p[1], NULL, MIN(splice_size, 16384), 0);

so you limit the amount of data you ask for.

This makes it work for me on 2.6.31.

metadaddy