[Libssh] [PATCH 2/2] An example of using the new poll code.

Aleksandar Kanchev aleksandar.kanchev at googlemail.com
Wed Jun 24 16:44:32 CEST 2009


It has nothing to do with the SSH protocol itself, but demonstrates
how the new poll code could be used within libssh.

It implements a simple echo server that accepts any number of clients,
which are handled asynchronously.

Signed-off-by: Aleksandar Kanchev <aleksandar.kanchev at googlemail.com>
---
 CMakeLists.txt |    2 +
 samplepoll.c   |  303 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 305 insertions(+), 0 deletions(-)
 create mode 100644 samplepoll.c

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d935827..99a895d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -63,8 +63,10 @@ if (UNIX AND NOT WIN32)
   if (WITH_SFTP AND WITH_SERVER)
     add_executable(samplessh sample.c)
     add_executable(samplesshd samplesshd.c)
+    add_executable(samplepoll samplepoll.c)
 
     target_link_libraries(samplessh ${LIBSSH_SHARED_LIBRARY})
     target_link_libraries(samplesshd ${LIBSSH_SHARED_LIBRARY})
+    target_link_libraries(samplepoll ${LIBSSH_SHARED_LIBRARY})
   endif (WITH_SFTP AND WITH_SERVER)
 endif (UNIX AND NOT WIN32)
diff --git a/samplepoll.c b/samplepoll.c
new file mode 100644
index 0000000..0583623
--- /dev/null
+++ b/samplepoll.c
@@ -0,0 +1,303 @@
+/*
+ * samplepoll.c - demonstrates the use of libssh's poll mechanism.
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Aleksandar Kanchev
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ *
+ * vim: ts=2 sw=2 et cindent
+ */
+
+/* NOTE this has nothing to do with the SSH protocol itself */
+
+#include <libssh/libssh.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#define DEFAULT_PORT  12000
+#define BUFFER_SIZE   255         /* client buffer size */
+
+typedef struct {
+  char *host;
+  short port;
+  char *buf;
+  char *wptr;
+  int buf_len;
+} client_t;
+
+static void client_free(client_t *c) {
+  free(c->host);
+  free(c->buf);
+  free(c);
+}
+
+static void client_disconnect(SSH_POLL *p, int fd, client_t *c) {
+  fprintf(stderr, "Client %s:%hu disconnected\n", c->host, c->port);
+
+  close(fd);
+  client_free(c);
+  ssh_poll_ctx_remove(ssh_poll_get_ctx(p), p);
+  ssh_poll_free(p);
+}
+
+static int client_write(SSH_POLL *p, int fd, client_t *c) {
+  int r;
+
+  r = send(fd, c->wptr, c->buf_len, 0);
+  if (r <= 0) {
+    client_disconnect(p, fd, c);
+    return -1;
+  }
+
+  c->buf_len -= r;
+
+  if (c->buf_len > 0) {
+    c->wptr += r;
+    ssh_poll_remove_events(p, POLLIN);
+    ssh_poll_add_events(p, POLLOUT);
+  } else {
+    ssh_poll_remove_events(p, POLLOUT);
+    ssh_poll_add_events(p, POLLIN);
+  }
+
+  return 0;
+}
+
+static int client_read(SSH_POLL *p, int fd, client_t *c) {
+  int r;
+
+  r = recv(fd, c->buf, BUFFER_SIZE, 0);
+  if (r <= 0) {
+    client_disconnect(p, fd, c);
+    return -1;
+  }
+
+  printf("Read %d bytes from client %s:%hu\n", r, c->host, c->port);
+  c->buf_len = r;
+  c->wptr = c->buf;
+
+  return client_write(p, fd, c);
+}
+
+static int client_handler(SSH_POLL *p, int fd, int revents, void *userdata) {
+  client_t *c = userdata;
+
+  if (revents & (POLLERR | POLLHUP)) {
+    client_disconnect(p, fd, c);
+    return -1;
+  }
+
+  if (revents & POLLIN) {
+    return client_read(p, fd, c);
+  }
+
+  if (revents & POLLOUT) {
+    return client_write(p, fd, c);
+  }
+
+  return 0;
+}
+
+static client_t *client_new(const struct sockaddr_in *addr) {
+  client_t *c;
+
+  c = malloc(sizeof(client_t));
+  if (!c) {
+    return NULL;
+  }
+
+  c->host = strdup(inet_ntoa(addr->sin_addr));
+  if (!c->host) {
+    free(c);
+    return NULL;
+  }
+
+  c->port = ntohs(addr->sin_port);
+
+  c->buf = malloc(BUFFER_SIZE);
+  if (!c->buf) {
+    free(c->host);
+    free(c);
+    return NULL;
+  }
+
+  return c;
+}
+
+static int bind_accept(SSH_POLL_CTX *ctx, int fd) {
+  int cli_fd;
+  struct sockaddr_in caddr;
+  socklen_t caddr_len;
+  int flags;
+  client_t *c;
+  SSH_POLL *pc;
+
+  caddr_len = sizeof(struct sockaddr_in);
+  cli_fd = accept(fd, (struct sockaddr *) &caddr, &caddr_len);
+  if (cli_fd < 0) {
+    perror("accept()");
+    return -1;
+  }
+
+  if ((flags = fcntl(cli_fd, F_GETFL)) < 0) {
+       perror("fcntl(F_GETFL)");
+       close(cli_fd);
+       return -1;
+   }
+
+   flags |= O_NONBLOCK;
+   if (fcntl(cli_fd, F_SETFL, flags) < 0) {
+       perror("fcntl(F_SETFL)");
+       close(cli_fd);
+       return -1;
+   }
+
+   c = client_new(&caddr);
+   if (!c) {
+     close(cli_fd);
+     return -1;
+   }
+
+   pc = ssh_poll_new(cli_fd, POLLIN | POLLERR | POLLHUP, client_handler, c);
+   if (!pc) {
+     fprintf(stderr, "Unable to allocate client poll\n");
+     client_free(c);
+     close(cli_fd);
+     return -1;
+   }
+
+   if (ssh_poll_ctx_add(ctx, pc) < 0) {
+     fprintf(stderr, "Unable to add client poll to context\n");
+     client_free(c);
+     close(cli_fd);
+     ssh_poll_free(pc);
+     return -1;
+   }
+
+   printf("Accepted client %s:%hu\n", c->host, c->port);
+
+   return 0;
+}
+
+static int bind_handler(SSH_POLL *p, int fd, int revents, void *unused) {
+  if (revents & (POLLERR | POLLHUP)) {
+    fprintf(stderr, "error on bind sock\n");
+    ssh_poll_ctx_remove(ssh_poll_get_ctx(p), p);
+    ssh_poll_free(p);
+    exit(-1);
+  }
+
+  if (revents & POLLIN) {
+    if (bind_accept(ssh_poll_get_ctx(p), fd) < 0) {
+      /* ignore client */
+    }
+  }
+
+  if (unused != NULL) {
+    /* ignore compiler warning */
+  }
+
+  return 0;
+}
+
+static int bind_listen(int port, SSH_POLL_CTX *ctx) {
+  int sock;
+  struct sockaddr_in saddr;
+  SSH_POLL *p;
+
+  sock = socket(AF_INET, SOCK_STREAM, 0);
+  if (sock < 0) {
+    perror("socket()");
+    return -1;
+  }
+
+  memset(&saddr, 0, sizeof(saddr));
+  saddr.sin_family = AF_INET;
+  saddr.sin_port = htons((short) port);
+
+  if (bind(sock, (const struct sockaddr *) &saddr, sizeof(struct sockaddr_in))
+      < 0) {
+    perror("bind()");
+    close(sock);
+    return -1;
+  }
+
+  if (listen(sock, 1) < 0) {
+    perror("listen()");
+    close(sock);
+    return -1;
+  }
+
+  p = ssh_poll_new(sock, POLLIN | POLLERR | POLLHUP, bind_handler, NULL);
+  if (!p) {
+    fprintf(stderr, "Unable to allocate bind poll\n");
+    close(sock);
+    return -1;
+  }
+
+  if (ssh_poll_ctx_add(ctx, p) < 0) {
+    fprintf(stderr, "Unable to add bind to poll context\n");
+    close(sock);
+    ssh_poll_free(p);
+    return -1;
+  }
+
+  return 0;
+}
+
+int main(int argc, char **argv) {
+  int port;
+  SSH_POLL_CTX *ctx;
+
+  if (argc > 1) {
+    port = atoi(argv[1]);
+  } else {
+    port = DEFAULT_PORT;
+  }
+
+  ctx = ssh_poll_ctx_new(0);
+  if (!ctx) {
+    fprintf(stderr, "Unable to allocate poll context!\n");
+    return -1;
+  }
+
+  if (bind_listen(port, ctx) < 0) {
+    fprintf(stderr, "Unable to bind on port %d\n", port);
+    ssh_poll_ctx_free(ctx);
+    return -1;
+  }
+
+  printf("Waiting on port %d ...\n", port);
+
+  while (1) {
+    if (ssh_poll_ctx(ctx, -1) < 0) {
+      perror("poll()");
+      break;
+    }
+  }
+
+  ssh_poll_ctx_free(ctx);
+
+  return -1;
+}
-- 
1.6.2.2




More information about the Libssh mailing list