]> git.proxmox.com Git - mirror_frr.git/commitdiff
lib: allow returning a file descriptor over vtysh
authorDavid Lamparter <equinox@diac24.net>
Tue, 3 Dec 2019 23:17:50 +0000 (00:17 +0100)
committerDavid Lamparter <equinox@opensourcerouting.org>
Mon, 28 Feb 2022 12:28:40 +0000 (13:28 +0100)
This adds the plumbing necessary to yield back a file descriptor to
vtysh.  The fd is passed on the command status code bytes through
AF_UNIX SCM_RIGHTS.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
lib/vty.c
lib/vty.h

index a42b8c92be4ee2664fcd01d168769dc6290d45c1..ad9cd719e067430d082d8375c02fc56e172eb830 100644 (file)
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -1464,6 +1464,11 @@ static void vty_read(struct thread *thread)
                        vty_out(vty, "\n");
                        buffer_flush_available(vty->obuf, vty->wfd);
                        vty_execute(vty);
+
+                       if (vty->pass_fd != -1) {
+                               close(vty->pass_fd);
+                               vty->pass_fd = -1;
+                       }
                        break;
                case '\t':
                        vty_complete_command(vty);
@@ -1561,6 +1566,7 @@ struct vty *vty_new(void)
        new->obuf = buffer_new(0); /* Use default buffer size. */
        new->buf = XCALLOC(MTYPE_VTY, VTY_BUFSIZ);
        new->max = VTY_BUFSIZ;
+       new->pass_fd = -1;
 
        return new;
 }
@@ -2010,9 +2016,64 @@ static void vtysh_accept(struct thread *thread)
        vty_event(VTYSH_READ, vty);
 }
 
+static int vtysh_do_pass_fd(struct vty *vty)
+{
+       struct iovec iov[1] = {
+               {
+                       .iov_base = vty->pass_fd_status,
+                       .iov_len = sizeof(vty->pass_fd_status),
+               },
+       };
+       union {
+               uint8_t buf[CMSG_SPACE(sizeof(int))];
+               struct cmsghdr align;
+       } u;
+       struct msghdr mh = {
+               .msg_iov = iov,
+               .msg_iovlen = array_size(iov),
+               .msg_control = u.buf,
+               .msg_controllen = sizeof(u.buf),
+       };
+       struct cmsghdr *cmh = CMSG_FIRSTHDR(&mh);
+       ssize_t ret;
+
+       cmh->cmsg_level = SOL_SOCKET;
+       cmh->cmsg_type = SCM_RIGHTS;
+       cmh->cmsg_len = CMSG_LEN(sizeof(int));
+       memcpy(CMSG_DATA(cmh), &vty->pass_fd, sizeof(int));
+
+       ret = sendmsg(vty->wfd, &mh, 0);
+       if (ret < 0 && ERRNO_IO_RETRY(errno))
+               return BUFFER_PENDING;
+
+       close(vty->pass_fd);
+       vty->pass_fd = -1;
+       vty->status = VTY_NORMAL;
+
+       if (ret <= 0)
+               return BUFFER_ERROR;
+
+       /* resume accepting commands (suspended in vtysh_read) */
+       vty_event(VTYSH_READ, vty);
+
+       if ((size_t)ret < sizeof(vty->pass_fd_status)) {
+               size_t remains = sizeof(vty->pass_fd_status) - ret;
+
+               buffer_put(vty->obuf, vty->pass_fd_status + ret, remains);
+               return BUFFER_PENDING;
+       }
+       return BUFFER_EMPTY;
+}
+
 static int vtysh_flush(struct vty *vty)
 {
-       switch (buffer_flush_available(vty->obuf, vty->wfd)) {
+       int ret;
+
+       ret = buffer_flush_available(vty->obuf, vty->wfd);
+       if (ret == BUFFER_EMPTY && vty->status == VTY_PASSFD)
+               ret = vtysh_do_pass_fd(vty);
+
+       switch (ret) {
        case BUFFER_PENDING:
                vty_event(VTYSH_WRITE, vty);
                break;
@@ -2031,6 +2092,14 @@ static int vtysh_flush(struct vty *vty)
        return 0;
 }
 
+void vty_pass_fd(struct vty *vty, int fd)
+{
+       if (vty->pass_fd != -1)
+               close(vty->pass_fd);
+
+       vty->pass_fd = fd;
+}
+
 static void vtysh_read(struct thread *thread)
 {
        int ret;
@@ -2090,6 +2159,26 @@ static void vtysh_read(struct thread *thread)
                                printf("vtysh node: %d\n", vty->node);
 #endif /* VTYSH_DEBUG */
 
+                               if (vty->pass_fd != -1) {
+                                       memset(vty->pass_fd_status, 0, 4);
+                                       vty->pass_fd_status[3] = ret;
+                                       vty->status = VTY_PASSFD;
+
+                                       if (!vty->t_write)
+                                               vty_event(VTYSH_WRITE, vty);
+
+                                       /* this introduces a "sequence point"
+                                        * command output is written normally,
+                                        * read processing is suspended until
+                                        * buffer is empty
+                                        * then retcode + FD is written
+                                        * then normal processing resumes
+                                        *
+                                        * => skip vty_event(VTYSH_READ, vty)!
+                                        */
+                                       return;
+                               }
+
                                /* hack for asynchronous "write integrated"
                                 * - other commands in "buf" will be ditched
                                 * - input during pending config-write is
@@ -2161,6 +2250,11 @@ void vty_close(struct vty *vty)
        THREAD_OFF(vty->t_write);
        THREAD_OFF(vty->t_timeout);
 
+       if (vty->pass_fd != -1) {
+               close(vty->pass_fd);
+               vty->pass_fd = -1;
+       }
+
        /* Flush buffer. */
        buffer_flush_all(vty->obuf, vty->wfd);
 
index 9ffbce326861895738aa0d8e49f55d4fa0ae02c7..92fbb468a975538c421c462f1dd19f339d905f4b 100644 (file)
--- a/lib/vty.h
+++ b/lib/vty.h
@@ -161,7 +161,19 @@ struct vty {
        unsigned char escape;
 
        /* Current vty status. */
-       enum { VTY_NORMAL, VTY_CLOSE, VTY_MORE, VTY_MORELINE } status;
+       enum {
+               VTY_NORMAL,
+               VTY_CLOSE,
+               VTY_MORE,
+               VTY_MORELINE,
+               VTY_PASSFD,
+       } status;
+
+       /* vtysh socket/fd passing (for terminal monitor) */
+       int pass_fd;
+
+       /* CLI command return value (likely CMD_SUCCESS) when pass_fd != -1 */
+       uint8_t pass_fd_status[4];
 
        /* IAC handling: was the last character received the
           IAC (interpret-as-command) escape character (and therefore the next
@@ -329,6 +341,11 @@ extern bool vty_set_include(struct vty *vty, const char *regexp);
  */
 extern int vty_json(struct vty *vty, struct json_object *json);
 
+/* post fd to be passed to the vtysh client
+ * fd is owned by the VTY code after this and will be closed when done
+ */
+extern void vty_pass_fd(struct vty *vty, int fd);
+
 extern bool vty_read_config(struct nb_config *config, const char *config_file,
                            char *config_default_dir);
 extern void vty_time_print(struct vty *, int);