Perhaps this has been addressed in more recent kernels, but I don't
have the bandwith to do frequent updates. So I'll describe the
problem.

shutdown(int s, int how) does not work as advertised. According to the
man page, the second argument to shutdown() is interpreted as follows:

       If  how is 0, further receives will be disallowed.  If how
       is 1, further sends will be disallowed.  If how is 2, fur-
       ther sends and receives will be disallowed.

A test program, that attempts to create a socket pair and make it
one-way using the shutdown() call, is appended at the end of this
message. If shutdown() works, it will simply copy data from stdin to
stdout. On linux (Linux puck 2.0.33 #1 Tue Oct 27 07:22:00 CET 1998
sparc unknown), the program dies with a Broken pipe message as soon as
something is written to its stdin.

When I browsed the kernel source code, I found the problem. The second
argument to shutdowm() is passed untouched through sys_socket()
(net/socket.c) to unix_shutdown() (net/unix/af_unix.c) and to the
corresponding function in net/ipv4/af_inet.c, and I would imagine also
to any other protocols.

However, unix_shutdown() (and other corresponding functions) uses the
argument as a bitmap, with the bits defined in include/net/sock.h:

  #define SHUTDOWN_MASK 3
  #define RCV_SHUTDOWN  1
  #define SEND_SHUTDOWN 2

This means that to disallow further writes (for instance), you'll have
to use 2 == SEND_SHUTDOWN, not 1, as is said in the documentation. In
fact, all the threee defined values are off by one.

There are several ways to fix this; one would be to document the
behavoir and add symbolic constants to some user include file (but I
suspect that the documented values are somewhat "standard", so it
might be a bad idea to use other values). The easiest
way to solve the problem is probably to add a simple conversion in
sys_socket in net/socket.c:

/*
 *      Shutdown a socket.
 */

asmlinkage int sys_shutdown(int fd, int how)
{
        struct socket *sock;
        struct file *file;

        if (fd < 0 || fd >= NR_OPEN || ((file = current->files->fd[fd]) == NULL))
                return(-EBADF);
        if (!(sock = sockfd_lookup(fd, NULL)))
                return(-ENOTSOCK);

        return(sock->ops->shutdown(sock, how + 1));
Add this:                                   ^^^^
}

I also think it would be a good idea to add symbolic constants to some
user include file. For example, on Solaris, <sys/socket.h> defines
SHUT_RD, SHUT_WR and SHUT_RDWR.

Best regards,
/Niels M�ller


----->8-----
/* socket_test.c
 *
 */

#include <string.h>
#include <errno.h>

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

#include <stdio.h>

#define werror(format, args...) fprintf(stderr, format, ## args)

/* Creates a one-way socket connection. Returns 1 on successm 0 on
 * failure. fds[0] is for reading, fds[1] for writing (like for the
 * pipe() system call). */
static int make_pipe(int *fds)
{
  /* From the shutdown(2) man page*/
#define REC 0
#define SEND 1

  if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) < 0)
    {
      werror("socketpair() failed: %s\n", strerror(errno));
      return 0;
    }
  werror("Created socket pair. Using fd:s %d <-- %d\n", fds[0], fds[1]);

  if(shutdown(fds[0], SEND) < 0)
    {
      werror("shutdown(%d, SEND) failed: %s\n", fds[0], strerror(errno));
      return 0;
    }
  if (shutdown(fds[1], REC) < 0)
    {
      werror("shutdown(%d, REC) failed: %s\n", fds[1], strerror(errno));
      return 0;
    }

  return 1;
#undef REC
#undef SEND
}

void copy(char *label, int in, int out)
{
  werror("%s copying %d --> %d\n", label, in, out);
  while(1)
    {
      char buffer[1000];
      int n = read(in, buffer, 1000);

      switch(n)
        {
        case -1:
          werror("%s: read failed (errno = %d): %s\n",
                 label, errno, strerror(errno));
          exit(1);
        case 0:
          exit(0);
        default:
          if (write(out, buffer, n) < n)
            {
              werror("%s: Write failed.\n");
              exit(1);
            }
        }
    }
}

int main(int argc, char **argv)
{
  int fds[2];
  
  if (!make_pipe(fds))
    exit(1);

  switch(fork())
    {
    case -1:
      werror("fork() failed\n");
      exit(1);
    case 0:
      werror("Child\n");
      copy("child", fds[0], STDOUT_FILENO);
      abort();
    default:
      werror("Parent\n");
      copy("parent", STDIN_FILENO, fds[1]);
      abort();
    }
}
-
To unsubscribe from this list: send the line "unsubscribe linux-net" in
the body of a message to [EMAIL PROTECTED]

Reply via email to