summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore18
-rw-r--r--Makefile.am21
-rw-r--r--configure.ac33
-rw-r--r--src/Makefile.am23
-rw-r--r--src/channel.c241
-rw-r--r--src/channel.h8
-rw-r--r--src/channel.scm32
-rw-r--r--src/session.c382
-rw-r--r--src/session.h8
-rw-r--r--src/session.scm31
-rw-r--r--src/ssh-error.c31
-rw-r--r--src/ssh-error.h8
12 files changed, 836 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5300564
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,18 @@
+# -*- shell-script -*-
+
+# Backup files
+*\~
+
+# Object files and static libraries
+*.o
+
+# Auto-generated Makefile
+Makefile
+
+config.log
+config.status
+.deps
+autom4te.cache
+
+TAGS
+tags \ No newline at end of file
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..7d72ec0
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,21 @@
+## Config file for GNU Automake.
+##
+## Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+##
+## This file is part of libguile-ssh.
+##
+## libguile-ssh is free software: you can redistribute it and/or
+## modify it under the terms of the GNU General Public License as
+## published by the Free Software Foundation, either version 3 of the
+## License, or (at your option) any later version.
+##
+## libguile-ssh 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
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with libguile-ssh. If not, see <http://www.gnu.org/licenses/>.
+
+SUBDIRS = src
+
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..f995ee4
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,33 @@
+## Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+##
+## This file is part of libguile-ssh.
+##
+## libguile-ssh is free software: you can redistribute it and/or
+## modify it under the terms of the GNU General Public License as
+## published by the Free Software Foundation, either version 3 of the
+## License, or (at your option) any later version.
+##
+## libguile-ssh 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
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with libguile-ssh. If not, see <http://www.gnu.org/licenses/>.
+
+AC_INIT([libguile-ssh], [0.1], [poptsov.artyom@gmail.com])
+
+AM_INIT_AUTOMAKE
+
+AC_PROG_CC
+
+GUILE_PROGS
+GUILE_FLAGS
+
+# Check for libssh
+AC_SEARCH_LIBS([ssh_new], [ssh], [], AC_MSG_ERROR([libssh is not found.], [1]))
+
+AC_CONFIG_FILES([Makefile])
+
+# Generate a Makefile, based on the results.
+AC_OUTPUT()
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 0000000..f2492e5
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,23 @@
+## Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+##
+## This file is part of libguile-ssh.
+##
+## libguile-ssh is free software: you can redistribute it and/or
+## modify it under the terms of the GNU General Public License as
+## published by the Free Software Foundation, either version 3 of the
+## License, or (at your option) any later version.
+##
+## libguile-ssh 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
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with libguile-ssh. If not, see <http://www.gnu.org/licenses/>.
+
+AM_CFLAGS = @GUILE_CFLAGS@ -lssh `pkg-config --cflags guile-1.8`
+AM_LDFLAGS=`pkg-config --libs guile-1.8` -lssh
+
+lib_LTLIBRARIES = libguile-ssh.la
+
+libguile_ssh_la_SOURCES = channel.c session.c
diff --git a/src/channel.c b/src/channel.c
new file mode 100644
index 0000000..55c10e4
--- /dev/null
+++ b/src/channel.c
@@ -0,0 +1,241 @@
+/* channel.c -- SSH channel smob.
+ *
+ * Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+ *
+ * This file is part of libguile-ssh
+ *
+ * libguile-ssh is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * libguile-ssh 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libguile-ssh. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libguile.h>
+#include <libssh/libssh.h>
+
+#include "session.h"
+#include "channel.h"
+#include "ssh-error.h"
+
+static scm_t_bits channel_tag; /* Smob tag. */
+
+
+/* Smob specific procedures */
+
+SCM
+mark_channel (SCM channel_smob)
+{
+ return SCM_BOOL_F;
+}
+
+size_t
+free_channel (SCM channel_smob)
+{
+ struct channel_data *data
+ = (struct channel_data *) SCM_SMOB_DATA (channel_smob);
+
+ ssh_channel_free (data->ssh_channel);
+
+ return 0;
+}
+
+/* Allocate a new SSH channel. */
+SCM
+guile_ssh_make_channel (SCM session_smob)
+{
+ SCM smob;
+
+ struct session_data *session_data
+ = (struct session_data *) SCM_SMOB_DATA (session_smob);
+
+ struct channel_data *channel_data
+ = (struct channel_data *) scm_gc_malloc (sizeof (struct channel_data),
+ "channel");
+ channel_data->ssh_channel = ssh_channel_new (session_data->ssh_session);
+ if (channel_data->ssh_channel == NULL)
+ {
+ ssh_error (__func__, "Couldn't allocate a new channel.",
+ SCM_BOOL_F, SCM_BOOL_F);
+ }
+
+ SCM_NEWSMOB (smob, channel_tag, channel_data);
+
+ return smob;
+}
+
+
+/* SSH channel specific procedures. */
+
+/* Close a channel. */
+SCM
+guile_ssh_channel_close (SCM channel_smob)
+{
+ struct channel_data *data
+ = (struct channel_data *) SCM_SMOB_DATA (channel_smob);
+
+ int res; /* Result of a function call. */
+
+ res = ssh_channel_close (data->ssh_channel);
+
+ return res ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+SCM
+guile_ssh_channel_open_session (SCM channel_smob)
+{
+ struct channel_data *data
+ = (struct channel_data *) SCM_SMOB_DATA (channel_smob);
+
+ int res; /* Result of a function call. */
+
+ res = ssh_channel_open_session (data->ssh_channel);
+
+ return (res == SSH_OK) ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+/* Run a shell command without an interactive shell. */
+SCM
+guile_ssh_channel_request_exec (SCM channel_smob, SCM cmd)
+{
+ struct channel_data *data
+ = (struct channel_data *) SCM_SMOB_DATA (channel_smob);
+
+ int res; /* Result of a function call. */
+ char *c_cmd; /* Command to execute. */
+
+ SCM_ASSERT (scm_is_string (cmd), cmd, SCM_ARG2, __func__);
+
+ c_cmd = scm_to_locale_string (cmd);
+
+ res = ssh_channel_request_exec (data->ssh_channel, c_cmd);
+
+ free (c_cmd);
+
+ return (res == SSH_OK) ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+/* Poll a channel for data to read.
+ *
+ * Return amount of data that can be read, or #f on error.
+ */
+SCM
+guile_ssh_channel_pool (SCM channel_smob, SCM is_stderr)
+{
+ struct channel_data *data
+ = (struct channel_data *) SCM_SMOB_DATA (channel_smob);
+
+ int res; /* Result of a function call. */
+
+ SCM_ASSERT (scm_is_boolean (is_stderr), is_stderr, SCM_ARG2, __func__);
+
+ res = ssh_channel_poll (data->ssh_channel, scm_is_true (is_stderr));
+
+ if (res >= 0)
+ return scm_from_int (res);
+ else
+ return SCM_BOOL_F;
+}
+
+SCM
+guile_ssh_channel_read (SCM channel_smob, SCM count, SCM is_stderr)
+{
+ struct channel_data *data
+ = (struct channel_data *) SCM_SMOB_DATA (channel_smob);
+
+ int res; /* Result of a function call. */
+ char *buffer; /* Buffer for data. */
+ uint32_t c_count; /* Size of buffer. */
+ SCM data; /* Obtained data from the channel. */
+
+ scm_dynwind_begin (0);
+
+ SCM_ASSERT (scm_is_unsigned_integer (count, 0, UINT32_MAX), count,
+ SCM_ARG2, __func__);
+ SCM_ASSERT (scm_is_boolean (is_stderr), is_stderr, SCM_ARG3, __func__);
+
+ c_count = scm_to_unsigned_integer (count, 0, UINT32_MAX);
+
+ buffer = scm_gc_malloc (sizeof (char) * c_count, "data buffer");
+ scm_dynwind_free (buffer);
+
+ res = ssh_channel_read (data->ssh_channel, buffer, count,
+ scm_is_true (is_stderr));
+
+ if (res > 0)
+ {
+ data = scm_from_locale_string (buffer);
+ }
+ else if (res == 0)
+ {
+ data = SCM_BOOL_F;
+ }
+ else
+ {
+ ssh_error (__func__, "Couldn't read data from a channel.",
+ SCM_BOOL_F, SCM_BOOL_F);
+ }
+
+ scm_dynwind_end ();
+
+ return data;
+}
+
+
+/* Predicates */
+
+SCM
+guile_ssh_channel_is_open (SCM channel_smob)
+{
+ struct channel_data *data
+ = (struct channel_data *) SCM_SMOB_DATA (channel_smob);
+
+ int res; /* Result of a function call. */
+
+ res = ssh_channel_is_open (data->ssh_channel);
+
+ return res ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+SCM
+guile_ssh_channel_is_eof (SCM channel_smob)
+{
+ struct channel_data *data
+ = (struct channel_data *) SCM_SMOB_DATA (channel_smob);
+
+ int res; /* Result of a function call. */
+
+ res = ssh_channel_is_eof (data->ssh_channel);
+
+ return res ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+
+/* channel smob initialization. */
+void
+init_channel_type (void)
+{
+ channel_tag = scm_make_smob_type ("ssh:channel", sizeof (struct channel_data));
+ scm_set_smob_mark (channel_tag, mark_channel);
+ scm_set_smob_free (channel_tag, free_channel);
+
+ scm_c_define_gsubr ("ssh:make-channel", 1, 0, 0, guile_ssh_make_channel);
+ scm_c_define_gsubr ("ssh:close-channel!", 1, 0, 0, guile_ssh_channel_close);
+ scm_c_define_gsubr ("ssh:channel-request-exec", 2, 0, 0,
+ guile_ssh_channel_request_exec);
+
+ scm_c_define_gsubr ("ssh:channel-poll", 2, 0, 0, guile_ssh_channel_pool);
+ scm_c_define_gsubr ("ssh:channel-read", 3, 0, 0, guile_ssh_channel_read);
+
+ scm_c_define_gsubr ("ssh:channel-open?", 1, 0, 0, guile_ssh_channel_is_open);
+ scm_c_define_gsubr ("ssh:channel-eof?", 1, 0, 0, guile_ssh_channel_is_eof);
+}
+
+/* channel.c ends here */
diff --git a/src/channel.h b/src/channel.h
new file mode 100644
index 0000000..f0df8ac
--- /dev/null
+++ b/src/channel.h
@@ -0,0 +1,8 @@
+#ifndef __CHANNEL_H__
+#define __CHANNEL_H__
+
+struct channel_data {
+ ssh_channel ssh_channel;
+};
+
+#endif /* ifndef __CHANNEL_H__ */
diff --git a/src/channel.scm b/src/channel.scm
new file mode 100644
index 0000000..7e45387
--- /dev/null
+++ b/src/channel.scm
@@ -0,0 +1,32 @@
+;;; channel.scm --
+
+;; Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+;;
+;;
+
+
+;;; Commentary:
+
+;;
+;;
+;; These methods are exported:
+;;
+;;
+
+
+;;; Code:
+
+(define-module (ssh channel)
+ #:use-module (ssh session)
+ #:export (ssh:channel
+ ssh:make-channel
+ ssh:close-channel!
+ ssh:channel-request-exec
+ ssh:channel-poll
+ ssh:channel-read
+ ssh:channel-open?
+ ssh:channel-eof?))
+
+(load-extension "libguile-ssh" "init_channel_type")
+
+;;; channel.scm ends here.
diff --git a/src/session.c b/src/session.c
new file mode 100644
index 0000000..4d8cec2
--- /dev/null
+++ b/src/session.c
@@ -0,0 +1,382 @@
+/* session.c -- SSH session smob.
+ *
+ * Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+ *
+ * This file is part of libguile-ssh
+ *
+ * libguile-ssh is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * libguile-ssh 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libguile-ssh. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libguile.h>
+#include <libssh/libssh.h>
+#include <string.h>
+
+#include "session.h"
+#include "ssh-error.h"
+
+#define PRINT_DEBUG(data)\
+ scm_display (data, scm_current_output_port ())
+
+static scm_t_bits session_tag; /* Smob tag. */
+
+/* SSH options mapping to Guile symbols. */
+struct option session_options[] = {
+ { "host", SSH_OPTIONS_HOST },
+ { "port", SSH_OPTIONS_PORT },
+ { "port-str", SSH_OPTIONS_PORT_STR },
+ { "fd", SSH_OPTIONS_FD },
+ { "user", SSH_OPTIONS_USER },
+ { "ssh-dir", SSH_OPTIONS_SSH_DIR },
+ { "identity", SSH_OPTIONS_IDENTITY },
+ { "add-identity", SSH_OPTIONS_ADD_IDENTITY },
+ { "knownhosts", SSH_OPTIONS_KNOWNHOSTS },
+ { "timeout", SSH_OPTIONS_TIMEOUT },
+ { "timeout-usec", SSH_OPTIONS_TIMEOUT_USEC },
+ { "ssh1", SSH_OPTIONS_SSH1 },
+ { "ssh2", SSH_OPTIONS_SSH2 },
+ { "log-verbosity", SSH_OPTIONS_LOG_VERBOSITY },
+ { "log-verbosity-str", SSH_OPTIONS_LOG_VERBOSITY_STR },
+ { "ciphers-c-s", SSH_OPTIONS_CIPHERS_C_S },
+ { "ciphers-s-c", SSH_OPTIONS_CIPHERS_S_C },
+ { "compression-c-s", SSH_OPTIONS_COMPRESSION_C_S },
+ { "compression-s-c", SSH_OPTIONS_COMPRESSION_S_C },
+ { "proxycommand", SSH_OPTIONS_PROXYCOMMAND },
+ { "bindaddr", SSH_OPTIONS_BINDADDR },
+ { "strcthostkeycheck", SSH_OPTIONS_STRICTHOSTKEYCHECK },
+ { "compression", SSH_OPTIONS_COMPRESSION },
+ { "compression-level", SSH_OPTIONS_COMPRESSION_LEVEL },
+ { NULL, -1 }
+};
+
+SCM
+mark_session (SCM session_smob)
+{
+ return SCM_BOOL_F;
+}
+
+/* Handle GC'ing of the session smob. */
+size_t
+free_session (SCM session_smob)
+{
+ struct session_data *data
+ = (struct session_data *) SCM_SMOB_DATA (session_smob);
+
+ ssh_disconnect (data->ssh_session);
+ ssh_free (data->ssh_session);
+ return 0;
+}
+
+/* Blocking flush of the outgoing buffer. */
+SCM
+guile_ssh_blocking_flush (SCM session_smob, SCM timeout)
+{
+ struct session_data *data
+ = (struct session_data *) SCM_SMOB_DATA (session_smob);
+
+ int c_timeout; /* Timeout */
+ int res; /* Result of a function call. */
+
+ SCM_ASSERT (scm_is_integer (timeout), timeout, SCM_ARG2, __func__);
+
+ c_timeout = scm_to_int (timeout);
+
+ res = scm_blockign_flush (data->ssh_session, c_timeout);
+ switch (res)
+ {
+ case SSH_OK:
+ return scm_from_locale_symbol ("ok");
+
+ case SSH_ERROR:
+ return scm_from_locale_symbol ("error");
+
+ case SSH_AGAIN:
+ return scm_from_locale_symbol ("again");
+ }
+}
+
+/* Set an SSH session option. */
+static void
+set_option (ssh_session session, int type, SCM value)
+{
+#define OPTION_ASSERT(pred, value) \
+ do { SCM_ASSERT (pred, value, SCM_ARG2, __func__); } while (0)
+
+ int res = 0; /* Result of an option setting */
+
+ switch (type)
+ {
+ case SSH_OPTIONS_HOST:
+ {
+ char *host;
+
+ OPTION_ASSERT (scm_is_string (value), value);
+
+ host = scm_to_locale_string (value);
+
+ res = ssh_options_set (session, type, host);
+ free (host);
+
+ if (res < 0)
+ goto err;
+ }
+ return;
+
+ case SSH_OPTIONS_PORT:
+ {
+ unsigned int port;
+
+ OPTION_ASSERT (scm_is_unsigned_integer (value, 0, UINT32_MAX), value);
+
+ port = scm_to_unsigned_integer (value, 0, UINT32_MAX);
+ res = ssh_options_set (session, type, &port);
+ if (res < 0)
+ goto err;
+ }
+ return;
+
+ case SSH_OPTIONS_PORT_STR:
+ {
+ char *port_str = scm_to_locale_string (value);
+
+ OPTION_ASSERT (scm_is_string (value), value);
+
+ res = ssh_options_set (session, type, port_str);
+ free (port_str);
+ if (res < 0)
+ goto err;
+ }
+ return;
+
+ case SSH_OPTIONS_USER:
+ {
+ char *user;
+
+ OPTION_ASSERT (scm_is_string (value), value);
+
+ user = scm_to_locale_string (value);
+ res = ssh_options_set (session, type, user);
+ free (user);
+ if (res < 0)
+ goto err;
+ }
+ return;
+
+ case SSH_OPTIONS_LOG_VERBOSITY:
+ {
+ int verbosity_level;
+
+ OPTION_ASSERT (scm_is_integer (value), value);
+
+ verbosity_level = scm_to_int (value);
+ res = ssh_options_set (session, type, &verbosity_level);
+ if (res < 0)
+ goto err;
+ }
+ return;
+
+ case SSH_OPTIONS_LOG_VERBOSITY_STR:
+ {
+ char *verbosity_str;
+
+ OPTION_ASSERT (scm_is_string (value), value);
+
+ verbosity_str = scm_to_locale_string (value);
+ res = ssh_options_set (session, type, verbosity_str);
+ free (verbosity_str);
+ if (res < 0)
+ goto err;
+ }
+ return;
+
+ /* TODO: Implement all this. */
+
+ case SSH_OPTIONS_FD:
+ case SSH_OPTIONS_SSH_DIR:
+ case SSH_OPTIONS_IDENTITY:
+ case SSH_OPTIONS_ADD_IDENTITY:
+ case SSH_OPTIONS_KNOWNHOSTS:
+ case SSH_OPTIONS_TIMEOUT:
+ case SSH_OPTIONS_TIMEOUT_USEC:
+ case SSH_OPTIONS_SSH1:
+ case SSH_OPTIONS_SSH2:
+ case SSH_OPTIONS_CIPHERS_C_S:
+ case SSH_OPTIONS_CIPHERS_S_C:
+ case SSH_OPTIONS_COMPRESSION_C_S:
+ case SSH_OPTIONS_COMPRESSION_S_C:
+ case SSH_OPTIONS_PROXYCOMMAND:
+ case SSH_OPTIONS_BINDADDR:
+ case SSH_OPTIONS_STRICTHOSTKEYCHECK:
+ case SSH_OPTIONS_COMPRESSION:
+ case SSH_OPTIONS_COMPRESSION_LEVEL:
+ goto notsupported;
+ }
+
+ err:
+ ssh_error (__func__, "Couldn't set an option.",
+ SCM_BOOL_F, SCM_BOOL_F);
+ return;
+
+ notsupported:
+ ssh_error (__func__, "Operation is not supported yet.",
+ SCM_BOOL_F, SCM_BOOL_F);
+
+#undef OPTION_ASSERT
+}
+
+/* Set a SSH option. */
+SCM
+guile_ssh_session_set (SCM session_smob, SCM type, SCM value)
+{
+ struct session_data* data
+ = (struct session_data *) SCM_SMOB_DATA (session_smob);
+
+ char *c_type_name; /* Name of an option */
+ struct option *option; /* SSH option mapping */
+ int is_found = 0; /* Is a parameter found? */
+ int res; /* Result of a function call */
+
+ if (scm_symbol_p (type) == SCM_BOOL_F)
+ {
+ ssh_error (__func__, "Wrong function call: "
+ "expected symbol as the second parameter",
+ SCM_BOOL_F, SCM_BOOL_F);
+ }
+
+ c_type_name = scm_to_locale_string (scm_symbol_to_string (type));
+
+ for (option = session_options; option->symbol != NULL; ++option)
+ {
+ if (! strcmp (c_type_name, option->symbol))
+ {
+ is_found = 1;
+ break;
+ }
+ }
+
+ if(! is_found)
+ {
+ ssh_error (__func__, "Wrong option",
+ SCM_BOOL_F, SCM_BOOL_F);
+ }
+
+ /* FIXME: There is an error here. */
+ scm_remember_upto_here_1 (session_smob);
+
+ return SCM_UNDEFINED;
+}
+
+/* Get SSH version.
+ *
+ * Return 1 for SSH1, 2 for SSH2 or negative value on error.
+ */
+SCM
+guile_ssh_get_version (SCM session_smob)
+{
+ struct session_data* data
+ = (struct session_data *) SCM_SMOB_DATA (session_smob);
+
+ int version = ssh_get_version (data->ssh_session);
+
+ return scm_from_int (version);
+}
+
+/* Create a new session. */
+SCM
+guile_ssh_make_session (void)
+{
+ SCM smob;
+
+ struct session_data *session_data
+ = (struct session_data *) scm_gc_malloc (sizeof (struct session_data),
+ "session");
+
+ session_data->ssh_session = ssh_new ();
+ if (session_data->ssh_session == NULL)
+ {
+ ssh_error (__func__, "Couldn't create a new SSH session.",
+ SCM_BOOL_F, SCM_BOOL_F);
+ }
+
+ SCM_NEWSMOB (smob, session_tag, session_data);
+
+ return smob;
+}
+
+/* Connect to the SSH server. */
+SCM
+guile_ssh_connect (SCM session_smob)
+{
+ struct session_data* data
+ = (struct session_data *) SCM_SMOB_DATA (session_smob);
+
+ int res = ssh_connect (data->ssh_session);
+
+ switch (res)
+ {
+ case SSH_OK:
+ return scm_from_locale_symbol ("ok");
+
+ case SSH_ERROR:
+ return scm_from_locale_symbol ("error");
+
+ case SSH_AGAIN:
+ return scm_from_locale_symbol ("again");
+ }
+}
+
+/* Disconnect from a session (client or server). */
+SCM
+guile_ssh_disconnect (SCM session_smob)
+{
+ struct session_data* data
+ = (struct session_data *) SCM_SMOB_DATA (session_smob);
+ ssh_disconnect (data->ssh_session);
+ return SCM_UNDEFINED;
+}
+
+
+/* Predicates */
+
+/* Check if we are connected. */
+SCM
+guile_ssh_is_connected (SCM session_smob)
+{
+ struct session_data* data
+ = (struct session_data *) SCM_SMOB_DATA (session_smob);
+
+ int res = ssh_is_connected (data->ssh_session);
+
+ return res ? SCM_BOOL_T : SCM_BOOL_F;
+}
+
+
+/* session smob initialization. */
+void
+init_session_type (void)
+{
+ session_tag = scm_make_smob_type ("ssh:session", sizeof (struct session_data));
+ scm_set_smob_mark (session_tag, mark_session);
+ scm_set_smob_free (session_tag, free_session);
+
+ scm_c_define_gsubr ("ssh:make-session", 0, 0, 0, guile_ssh_make_session);
+ scm_c_define_gsubr ("ssh:blocking-flush!", 2, 0, 0, guile_ssh_blocking_flush);
+ scm_c_define_gsubr ("ssh:session-set!", 3, 0, 0, guile_ssh_session_set);
+ scm_c_define_gsubr ("ssh:get-version", 1, 0, 0, guile_ssh_get_version);
+ scm_c_define_gsubr ("ssh:connect!", 1, 0, 0, guile_ssh_connect);
+ scm_c_define_gsubr ("ssh:disconnect!", 1, 0, 0, guile_ssh_disconnect);
+
+ scm_c_define_gsubr ("ssh:connected?", 1, 0, 0, guile_ssh_is_connected);
+}
+
+/* session.c ends here */
diff --git a/src/session.h b/src/session.h
new file mode 100644
index 0000000..b99f74d
--- /dev/null
+++ b/src/session.h
@@ -0,0 +1,8 @@
+struct session_data {
+ ssh_session ssh_session;
+};
+
+struct option {
+ char* symbol;
+ int type;
+};
diff --git a/src/session.scm b/src/session.scm
new file mode 100644
index 0000000..576e9e9
--- /dev/null
+++ b/src/session.scm
@@ -0,0 +1,31 @@
+;;; session.scm --
+
+;; Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+;;
+;;
+
+
+;;; Commentary:
+
+;;
+;;
+;; These methods are exported:
+;;
+;;
+
+
+;;; Code:
+
+(define-module (ssh session)
+ #:export (ssh:session
+ ssh:make-session
+ ssh:blocking-flush!
+ ssh:session-set!
+ ssh:get-version
+ ssh:connect!
+ ssh:disconnect!
+ ssh:connected?))
+
+(load-extension "libguile-ssh" "init_session_type")
+
+;;; session.scm ends here
diff --git a/src/ssh-error.c b/src/ssh-error.c
new file mode 100644
index 0000000..ae212a8
--- /dev/null
+++ b/src/ssh-error.c
@@ -0,0 +1,31 @@
+/* ssh-error.c -- Error reporting to Guile.
+ *
+ * Copyright (C) 2013 Artyom V. Poptsov <poptsov.artyom@gmail.com>
+ *
+ * This file is part of libguile-ssh
+ *
+ * LazyCat is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * LazyCat 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with LazyCat. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ssh-error.h"
+
+/* Report an error */
+inline void
+ssh_error (const char *subr, char *message, SCM args, SCM rest)
+{
+ SCM key = scm_from_locale_symbol (GUILE_SSH_EXCEPTION);
+ scm_error (key, subr, message, args, rest);
+}
+
+/* ssh-error.c ends here */
diff --git a/src/ssh-error.h b/src/ssh-error.h
new file mode 100644
index 0000000..73cf991
--- /dev/null
+++ b/src/ssh-error.h
@@ -0,0 +1,8 @@
+#ifndef __SSH_ERROR_H__
+#define __SSH_ERROR_H__
+
+#define GUILE_SSH_EXCEPTION "guile-ssh-exception"
+
+inline void ssh_error (const char *subr, char *message, SCM args, SCM rest);
+
+#endif /* ifndef __SSH_ERROR_H__ */