Implement shutdown() (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 22 Mar 2016 19:39:22 +0000 (15:39 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Tue, 22 Mar 2016 19:47:38 +0000 (15:47 -0400)
We had been just closing the FD, which is clearly wrong (you may have been
getting warnings from the kernel about this).  Best case, you just get a
warning.  Worst case, you accidentally close another FD in a concurrent
program.

I don't know if the TCP code is right.  It sends a FIN.  Maybe it doesn't
send it the right way, or maybe we should do things for the other states
too.

It's better than it was before.

Rebuild glibc.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/include/ip.h
kern/src/net/devip.c
kern/src/net/tcp.c
tools/compilers/gcc-glibc/glibc-2.19-akaros/sysdeps/akaros/shutdown.c

index a9dac77..d7fbf55 100644 (file)
@@ -66,6 +66,12 @@ enum {
        Connected = 4,
 };
 
+enum {
+       SHUT_RD = 0,
+       SHUT_WR = 1,
+       SHUT_RDWR = 2,
+};
+
 /*
  *  one per conversation directory
  */
@@ -295,6 +301,7 @@ struct Proto {
        int (*state) (struct conv *, char *unused_char_p_t, int);
        void (*create) (struct conv *);
        void (*close) (struct conv *);
+       void (*shutdown)(struct conv *, int);
        void (*rcv) (struct Proto *, struct Ipifc *, struct block *);
        void (*ctl)(struct conv *, char **, int);
        void (*advise) (struct Proto *, struct block *, char *unused_char_p_t);
index e68bc29..3a9da79 100644 (file)
@@ -1149,6 +1149,31 @@ err:
        error(EINVAL, "nonblock [on|off]");
 }
 
+static void shutdownctlmsg(struct conv *cv, struct cmdbuf *cb)
+{
+       if (cb->nf < 2)
+               goto err;
+       if (!strcmp(cb->f[1], "rd")) {
+               qhangup(cv->rq, "shutdown");
+               if (cv->p->shutdown)
+                       cv->p->shutdown(cv, SHUT_RD);
+       } else if (!strcmp(cb->f[1], "wr")) {
+               qhangup(cv->wq, "shutdown");
+               if (cv->p->shutdown)
+                       cv->p->shutdown(cv, SHUT_WR);
+       } else if (!strcmp(cb->f[1], "rdwr")) {
+               qhangup(cv->rq, "shutdown");
+               qhangup(cv->wq, "shutdown");
+               if (cv->p->shutdown)
+                       cv->p->shutdown(cv, SHUT_RDWR);
+       } else {
+               goto err;
+       }
+       return;
+err:
+       error(EINVAL, "shutdown [rx|tx|rxtx]");
+}
+
 static void tosctlmsg(struct conv *c, struct cmdbuf *cb)
 {
        if (cb->nf < 2)
@@ -1217,6 +1242,8 @@ static long ipwrite(struct chan *ch, void *v, long n, int64_t off)
                                bindctlmsg(x, c, cb);
                        else if (strcmp(cb->f[0], "nonblock") == 0)
                                nonblockctlmsg(c, cb);
+                       else if (strcmp(cb->f[0], "shutdown") == 0)
+                               shutdownctlmsg(c, cb);
                        else if (strcmp(cb->f[0], "ttl") == 0)
                                ttlctlmsg(c, cb);
                        else if (strcmp(cb->f[0], "tos") == 0)
index 823c05f..ca219d5 100644 (file)
@@ -501,6 +501,27 @@ static void tcpannounce(struct conv *c, char **argv, int argc)
        Fsconnected(c, NULL);
 }
 
+static void tcpshutdown(struct conv *c, int how)
+{
+       Tcpctl *tcb = (Tcpctl*)c->ptcl;
+
+       /* Do nothing for the read side */
+       if (how == SHUT_RD)
+               return;
+       /* Sends a FIN.  If we're in another state (like Listen), we'll run into
+        * issues, since we'll never send the FIN.  We'll be shutdown on our end,
+        * but we'll never tell the distant end.  Might just be an app issue. */
+       switch (tcb->state) {
+       case Syn_received:
+       case Established:
+               tcb->flgcnt++;
+               tcb->snd.nxt++;
+               tcpsetstate(c, Finwait1);
+               tcpoutput(c);
+               break;
+       }
+}
+
 /*
  *  tcpclose is always called with the q locked
  */
@@ -3178,6 +3199,7 @@ void tcpinit(struct Fs *fs)
        tcp->state = tcpstate;
        tcp->create = tcpcreate;
        tcp->close = tcpclose;
+       tcp->shutdown = tcpshutdown;
        tcp->rcv = tcpiput;
        tcp->advise = tcpadvise;
        tcp->stats = tcpstats;
index d9b1089..744a9a0 100644 (file)
@@ -1,24 +1,60 @@
-/* 
- * This file is part of the UCB release of Plan 9. It is subject to the license
- * terms in the LICENSE file found in the top-level directory of this
- * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
- * part of the UCB release of Plan 9, including this file, may be copied,
- * modified, propagated, or distributed except according to the terms contained
- * in the LICENSE file.
- */
+/* Copyright (c) 2016 Google Inc.
+ * Barret Rhoden <brho@cs.berkeley.edu>
+ * See LICENSE for details.
+ *
+ * shutdown(). */
+
+#include <sys/socket.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 
 #include "plan9_sockets.h"
 
-/* Supposed to shut down all or part of the connection open on socket FD.
-   HOW determines what to shut down:
-     0 = No more receptions;
-     1 = No more transmissions;
-     2 = No more receptions or transmissions.
-   Returns 0 on success, -1 for errors.  */
+/* Shuts down all or part of the connection open on socket FD.  Returns 0 on
+ * success, -1 for errors. */
 int shutdown(int fd, int how)
 {
-       /* plan 9 doesn't do a shutdown.  if you shut it down, it's now closed. */
-       return close(fd);
+       int ret, ctlfd;
+       static const char rd_msg[] = "shutdown rd";
+       static const char wr_msg[] = "shutdown wr";
+       static const char rdwr_msg[] = "shutdown rdwr";
+       char *msg;
+       size_t msg_sz;
+       Rock *r;
+
+       switch (how) {
+       case SHUT_RD:
+               msg = rd_msg;
+               msg_sz = sizeof(rd_msg);
+               break;
+       case SHUT_WR:
+               msg = wr_msg;
+               msg_sz = sizeof(wr_msg);
+               break;
+       case SHUT_RDWR:
+               msg = rdwr_msg;
+               msg_sz = sizeof(rdwr_msg);
+               break;
+       default:
+               errno = EINVAL;
+               werrstr("shutdown has bad 'how' %d", how);
+               return -1;
+       }
+       r = _sock_findrock(fd, 0);
+       if (!r) {
+               errno = EBADF;
+               werrstr("Rock lookup failed");
+               return -1;
+       }
+       ctlfd = open(r->ctl, O_RDWR);
+       if (!ctlfd)
+               return -1;
+       ret = write(ctlfd, msg, msg_sz);
+       close(ctlfd);
+       if (ret != msg_sz)
+               return -1;
+       return 0;
 }