[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