Browse Source

Initial commit.

Signed-off-by: Bradley Smith <brad@brad-smith.co.uk>
Bradley Smith 10 years ago
commit
ef810b7316
14 changed files with 550 additions and 0 deletions
  1. 24 0
      .gitignore
  2. 3 0
      AUTHORS
  3. 22 0
      COPYING
  4. 3 0
      ChangeLog
  5. 0 0
      INSTALL
  6. 1 0
      Makefile.am
  7. 0 0
      NEWS
  8. 0 0
      README
  9. 137 0
      bootstrap.sh
  10. 14 0
      configure.ac
  11. 10 0
      src/Makefile.am
  12. 155 0
      src/mssh-window.c
  13. 51 0
      src/mssh-window.h
  14. 130 0
      src/mssh.c

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+Makefile
+Makefile.in
+aclocal.m4
+.deps
+.libs
+*.la
+*.lo
+*.o
+*~
+autom4te.cache
+config.guess
+config.h
+config.h.in
+config.log
+config.status
+config.sub
+configure
+depcomp
+install-sh
+libtool
+ltmain.sh
+missing
+src/mssh
+stamp-h1

+ 3 - 0
AUTHORS

@@ -0,0 +1,3 @@
+Founder and Lead Developer:
+----------------------------------------------------------------------------
+    Bradley Smith  <brad@brad-smith.co.uk> (United Kingdom)

+ 22 - 0
COPYING

@@ -0,0 +1,22 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the copyright notice,
+       this list of conditions and the following disclaimer.
+    2. Redistributions in binary form must reproduce the copyright notice,
+       this list of conditions and the following disclaimer in the
+       documentation and/or other materials provided with the distribution.
+    3. The name of the author may not be used to endorse or promote
+       products derived from this software without specific prior written
+       permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ 3 - 0
ChangeLog

@@ -0,0 +1,3 @@
+2009-08-02  Bradley Smith  <brad@brad-smith.co.uk>
+
+	Initial commit.

+ 0 - 0
INSTALL


+ 1 - 0
Makefile.am

@@ -0,0 +1 @@
+SUBDIRS = src

+ 0 - 0
NEWS


+ 0 - 0
README


+ 137 - 0
bootstrap.sh

@@ -0,0 +1,137 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+DIE=0
+SRCDIR=`dirname $0`
+BUILDDIR=`pwd`
+srcfile=src/mssh.c
+
+debug ()
+# print out a debug message if DEBUG is a defined variable
+{
+  if [ ! -z "$DEBUG" ]; then
+    echo "DEBUG: $1"
+  fi
+}
+
+version_check ()
+# check the version of a package
+# first argument : complain ('1') or not ('0')
+# second argument : package name (executable)
+# third argument : source download url
+# rest of arguments : major, minor, micro version
+{
+  COMPLAIN=$1
+  PACKAGE=$2
+  URL=$3
+  MAJOR=$4
+  MINOR=$5
+  MICRO=$6
+
+  WRONG=
+
+  debug "major $MAJOR minor $MINOR micro $MICRO"
+  VERSION=$MAJOR
+  if [ ! -z "$MINOR" ]; then VERSION=$VERSION.$MINOR; else MINOR=0; fi
+  if [ ! -z "$MICRO" ]; then VERSION=$VERSION.$MICRO; else MICRO=0; fi
+
+  debug "version $VERSION"
+  echo "+ checking for $PACKAGE >= $VERSION ... " | tr -d '\n'
+
+  ($PACKAGE --version) < /dev/null > /dev/null 2>&1 ||
+  {
+    echo
+    echo "You must have $PACKAGE installed to compile $package."
+    echo "Download the appropriate package for your distribution,"
+    echo "or get the source tarball at $URL"
+    return 1
+  }
+  # the following line is carefully crafted sed magic
+  pkg_version=`$PACKAGE --version|head -n 1|sed 's/([^)]*)//g;s/^[a-zA-Z\.\ \-\/]*//;s/ .*$//'`
+  debug "pkg_version $pkg_version"
+  pkg_major=`echo $pkg_version | cut -d. -f1`
+  pkg_minor=`echo $pkg_version | sed s/[-,a-z,A-Z].*// | cut -d. -f2`
+  pkg_micro=`echo $pkg_version | sed s/[-,a-z,A-Z].*// | cut -d. -f3`
+  [ -z "$pkg_minor" ] && pkg_minor=0
+  [ -z "$pkg_micro" ] && pkg_micro=0
+
+  debug "found major $pkg_major minor $pkg_minor micro $pkg_micro"
+
+  #start checking the version
+  if [ "$pkg_major" -lt "$MAJOR" ]; then
+    WRONG=1
+  elif [ "$pkg_major" -eq "$MAJOR" ]; then
+    if [ "$pkg_minor" -lt "$MINOR" ]; then
+      WRONG=1
+    elif [ "$pkg_minor" -eq "$MINOR" -a "$pkg_micro" -lt "$MICRO" ]; then
+      WRONG=1
+    fi
+  fi
+
+  if [ ! -z "$WRONG" ]; then
+   echo "found $pkg_version, not ok !"
+   if [ "$COMPLAIN" -eq "1" ]; then
+     echo
+     echo "You must have $PACKAGE $VERSION or greater to compile $package."
+     echo "Get the latest version from <$URL>."
+     echo
+   fi
+   return 1
+  else
+    echo "found $pkg_version, ok."
+  fi
+}
+
+version_check 1 "pkg-config" "http://pkgconfig.freedesktop.org/" 0 9 || DIE=1
+version_check 1 "autoconf" "ftp://ftp.gnu.org/pub/gnu/autoconf/" 2 56 || DIE=1
+version_check 1 "automake" "ftp://ftp.gnu.org/pub/gnu/automake/" 1 9 || DIE=1
+if [ "$DIE" -eq 1 ]; then
+  exit 1
+fi
+
+
+# Chdir to the srcdir, then run auto* tools.
+cd $SRCDIR
+
+[ -f $srcfile ] || {
+  echo "Are you sure $SRCDIR is a valid source directory?"
+  exit 1
+}
+
+echo "+ running aclocal ..."
+aclocal || {
+  echo
+  echo "aclocal failed - check that all needed development files are present on system"
+  exit 1
+}
+echo "+ running autoheader ... "
+autoheader || {
+  echo
+  echo "autoheader failed"
+  exit 1
+}
+echo "+ running autoconf ... "
+autoconf || {
+  echo
+  echo "autoconf failed"
+  exit 1
+}
+echo "+ running automake ... "
+automake -a -c || {
+  echo
+  echo "automake failed"
+  exit 1
+}
+
+# Chdir back to the builddir before the configure step.
+cd $BUILDDIR
+
+# now remove the cache, because it can be considered dangerous in this case
+echo "+ Tidying up ... "
+rm -fr autom4te.cache config.h.in\~
+
+echo
+echo "Done."
+
+exit 0
+

+ 14 - 0
configure.ac

@@ -0,0 +1,14 @@
+AC_INIT([MultiSSH], [0.1], [brad@brad-smith.co.uk], [mssh])
+AM_CONFIG_HEADER([config.h])
+AM_INIT_AUTOMAKE
+
+AC_PROG_CC
+
+PKG_CHECK_MODULES(MSSH, [gtk+-2.0 vte])
+AC_SUBST(MSSH_CFLAGS)
+AC_SUBST(MSSH_LIBS)
+
+AC_OUTPUT(
+    Makefile
+    src/Makefile
+)

+ 10 - 0
src/Makefile.am

@@ -0,0 +1,10 @@
+AM_CFLAGS = -pedantic-errors -Werror -Wall -Wfatal-errors -Wwrite-strings
+
+INCLUDES = $(MSSH_CFLAGS)
+
+bin_PROGRAMS = mssh
+
+mssh_SOURCES = mssh.c mssh-window.c
+mssh_LDADD = $(MSSH_LIBS)
+
+EXTRA_DIST = mssh-window.h

+ 155 - 0
src/mssh-window.c

@@ -0,0 +1,155 @@
+#include <string.h>
+#include <stdlib.h>
+
+#include <gdk/gdkkeysyms.h>
+
+#include "mssh-window.h"
+
+G_DEFINE_TYPE(MSSHWindow, mssh_window, GTK_TYPE_WINDOW)
+
+static void mssh_window_destroy(GtkWidget *widget, gpointer data)
+{
+	gtk_main_quit();
+}
+
+GtkWidget* mssh_window_new(void)
+{
+    return g_object_new(MSSH_TYPE_WINDOW, NULL);
+}
+
+gboolean key_press(GtkWidget *widget, GdkEventKey *event,
+	gpointer user_data)
+{
+	int i;
+	gboolean dummy;
+
+	MSSHWindow *window = MSSH_WINDOW(user_data);
+
+	for(i = 0; i < window->num_servers; i++)
+	{
+		if(window->terms[i] != NULL)
+		{
+			if(gtk_check_menu_item_get_active(
+				GTK_CHECK_MENU_ITEM(window->items[i])))
+			{
+				g_signal_emit_by_name(window->terms[i], "key-press-event",
+					event, &dummy);
+			}
+		}
+	}
+
+	return TRUE;
+}
+
+void vte_child_exited(VteTerminal *vte, gpointer user_data)
+{
+	int i;
+	char data[] = "\n[Child Exited]";
+	MSSHWindow *window = MSSH_WINDOW(user_data);
+	vte_terminal_feed(vte, data, strlen(data));
+
+	for(i = 0; i < window->num_servers; i++)
+	{
+		if(window->terms[i] == GTK_WIDGET(vte))
+		{
+			window->terms[i] = NULL;
+			break;
+		}
+	}
+}
+
+static void mssh_window_init(MSSHWindow* window)
+{
+	GtkAccelGroup *accel_group;
+
+	accel_group = gtk_accel_group_new();
+	window->vbox = gtk_vbox_new(FALSE, 0);
+	window->entry = gtk_entry_new();
+	window->menu_bar = gtk_menu_bar_new();
+	window->server_menu = gtk_menu_new();
+	window->file_menu = gtk_menu_new();
+	window->server_item = gtk_menu_item_new_with_label("Servers");
+	window->file_item = gtk_menu_item_new_with_label("File");
+	window->file_quit = gtk_image_menu_item_new_from_stock(GTK_STOCK_QUIT,
+		NULL);
+
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(window->file_item),
+		window->file_menu);
+	gtk_menu_item_set_submenu(GTK_MENU_ITEM(window->server_item),
+		window->server_menu);
+
+	gtk_menu_shell_append(GTK_MENU_SHELL(window->file_menu),
+		window->file_quit);
+    g_signal_connect(G_OBJECT(window->file_quit), "activate",
+        G_CALLBACK(mssh_window_destroy), NULL);
+	gtk_widget_add_accelerator(window->file_quit, "activate", accel_group,
+		GDK_W, GDK_CONTROL_MASK, GTK_ACCEL_VISIBLE);
+	gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
+
+	gtk_menu_bar_append(GTK_MENU_BAR(window->menu_bar),
+		window->file_item);
+	gtk_menu_bar_append(GTK_MENU_BAR(window->menu_bar),
+		window->server_item);
+
+	g_signal_connect(G_OBJECT(window->entry), "key-press-event",
+		G_CALLBACK(key_press), window);
+
+	gtk_box_pack_start(GTK_BOX(window->vbox), window->menu_bar,
+		FALSE, TRUE, 0);
+	gtk_box_pack_start(GTK_BOX(window->vbox), window->entry,
+		FALSE, TRUE, 2);
+
+    gtk_container_add(GTK_CONTAINER(window), window->vbox);
+
+    gtk_widget_set_size_request(GTK_WIDGET(window), 1024, 768);
+    gtk_window_set_title(GTK_WINDOW(window), "MSSH");
+}
+
+void mssh_window_new_session(MSSHWindow* window, char **env,
+	int num_servers, char **servers)
+{
+	char *args[3] = { NULL, NULL, NULL };
+	int i, j, k;
+	int rows = num_servers/2 + num_servers%2;
+
+	window->env = env;
+	window->num_servers = num_servers;
+	window->servers = servers;
+
+	args[0] = strdup("ssh");
+
+    window->table = gtk_table_new(rows, num_servers > 1 ? 2 : 1, TRUE);
+	gtk_box_pack_start(GTK_BOX(window->vbox), window->table,
+		TRUE, TRUE, 0);
+
+	for(i = 0; i < rows; i++)
+	{
+		for(j = 0; j < (num_servers > 1 ? 2 : 1); j++)
+		{
+			k = j + i*2;
+			args[1] = window->servers[k];
+			window->terms[k] = vte_terminal_new();
+		    g_signal_connect(G_OBJECT(window->terms[k]), "child-exited",
+				G_CALLBACK(vte_child_exited), window);
+			vte_terminal_fork_command(VTE_TERMINAL(window->terms[k]),
+				"ssh", args, window->env, NULL, FALSE, FALSE,
+				FALSE);
+			gtk_table_attach(GTK_TABLE(window->table), window->terms[k],
+				j, j+1, i, i+1,	GTK_FILL | GTK_EXPAND,
+				GTK_FILL | GTK_EXPAND, 2, 2);
+
+			window->items[k] = gtk_check_menu_item_new_with_label(
+				window->servers[k]);
+			gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(
+				window->items[k]), TRUE);
+			gtk_menu_shell_append(GTK_MENU_SHELL(window->server_menu),
+				window->items[k]);
+		}
+	}
+
+	free(args[0]);
+}
+
+static void mssh_window_class_init(MSSHWindowClass *klass)
+{
+}

+ 51 - 0
src/mssh-window.h

@@ -0,0 +1,51 @@
+#ifndef __MSSH_WINDOW_H__
+#define __MSSH_WINDOW_H__
+
+#include <gtk/gtk.h>
+#include <vte/vte.h>
+
+G_BEGIN_DECLS
+
+#define MSSH_TYPE_WINDOW            mssh_window_get_type()
+#define MSSH_WINDOW(obj)            G_TYPE_CHECK_INSTANCE_CAST(obj,\
+    MSSH_TYPE_WINDOW, MSSHWindow)
+#define MSSH_WINDOW_CLASS(klass)    G_TYPE_CHECK_CLASS_CAST(klass,\
+    MSSH_WINDOW_TYPE, MSSHWindowClass)
+#define IS_MSSH_WINDOW(obj)         G_TYPE_CHECK_INSTANCE_TYPE(obj,\
+    MSSH_TYPE_WINDOW)
+#define IS_MSSH_WINDOW_CLASS(klass) G_TYPE_CHECK_CLASS_TYPE(klass,\
+    MSSH_TYPE_WINDOW)
+
+typedef struct
+{
+    GtkWindow widget;
+    GtkWidget *vbox;
+    GtkWidget *entry;
+    GtkWidget *table;
+    GtkWidget *menu_bar;
+    GtkWidget *server_menu;
+    GtkWidget *file_menu;
+    GtkWidget *server_item;
+    GtkWidget *file_item;
+    GtkWidget *file_quit;
+	char **env;
+	char **servers;
+	int num_servers;
+	GtkWidget *items[32];
+	GtkWidget *terms[32];
+} MSSHWindow;
+
+typedef struct
+{
+    GtkWindowClass parent_class;
+} MSSHWindowClass;
+
+GType mssh_window_get_type(void) G_GNUC_CONST;
+
+GtkWidget* mssh_window_new(void);
+void mssh_window_new_session(MSSHWindow* window, char **env,
+	int num_servers, char **servers);
+
+G_END_DECLS
+
+#endif /* __MSSH_WINDOW_H__ */

+ 130 - 0
src/mssh.c

@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <gtk/gtk.h>
+
+#include "config.h"
+#include "mssh-window.h"
+
+#define PKGINFO		PACKAGE_NAME " " VERSION
+#define COPYRIGHT	"Copyright (C) 2009 Bradley Smith <brad@brad-smith.co.uk>"
+
+static void on_mssh_destroy(GtkWidget *widget, gpointer data)
+{
+    gtk_widget_hide(widget);
+    gtk_main_quit();
+}
+
+void usage(const char *argv0)
+{
+	fprintf(stderr, "%s\n", PKGINFO);
+	fprintf(stderr, "%s\n", COPYRIGHT);
+	fprintf(stderr, "An ssh client to issue the same commands to multiple servers\n\n");
+	fprintf(stderr, "Usage: %s [OPTION]... [HOSTS]\n\n", argv0);
+	fprintf(stderr,
+		"  -h, --help       Display this help and exit\n");
+	fprintf(stderr,
+		"  -V, --version    Output version information and exit\n");
+	fprintf(stderr, "\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
+	exit(EXIT_FAILURE);
+}
+
+int main(int argc, char* argv[], char* env[])
+{
+    GtkWidget* window;
+	int c, option_index = 0;
+	int i, nhosts = 0;
+	char **hosts = NULL;
+
+	static struct option long_options[] =
+    {
+    	{"help",	no_argument,	0, 'h'},
+    	{"version",	no_argument,	0, 'V'},
+        {0, 0, 0, 0}
+    };
+
+	for(;;)
+	{
+		c = getopt_long(argc, argv, "hV", long_options, &option_index);
+
+		if(c == -1)
+        	break;
+
+        switch(c)
+        {
+		case 'h':
+			usage(argv[0]);
+			break;
+		case 'V':
+			printf("%s\n\n", PKGINFO);
+			printf("%s\n\n", COPYRIGHT);
+			printf("Redistribution and use in source and binary forms, with or without\n");
+			printf("modification, are permitted provided that the following conditions are met:\n");
+			printf("\n");
+			printf("    1. Redistributions of source code must retain the copyright notice,\n");
+			printf("       this list of conditions and the following disclaimer.\n");
+			printf("    2. Redistributions in binary form must reproduce the copyright notice,\n");
+			printf("       this list of conditions and the following disclaimer in the\n");
+			printf("       documentation and/or other materials provided with the distribution.\n");
+			printf("    3. The name of the author may not be used to endorse or promote\n");
+			printf("       products derived from this software without specific prior written\n");
+			printf("       permission.\n");
+			printf("\n");
+			printf("THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n");
+			printf("IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n");
+			printf("OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN\n");
+			printf("NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n");
+			printf("SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED\n");
+			printf("TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR\n");
+			printf("PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\n");
+			printf("LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\n");
+			printf("NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\n");
+			printf("SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n");
+
+			exit(EXIT_SUCCESS);
+			break;
+		case '?':
+			printf("\n");
+			usage(argv[0]);
+			exit(EXIT_FAILURE);
+			break;
+		default:
+			abort();
+		}
+	}
+
+	if (optind < argc)
+	{
+		hosts = malloc(sizeof(char*) * (argc - optind));
+		while (optind < argc)
+		{
+			hosts[nhosts++] = strdup(argv[optind++]);
+		}
+	}
+	else
+	{
+		fprintf(stderr, "No hosts specified\n\n");
+		usage(argv[0]);
+	}
+
+    gtk_init(&argc, &argv);
+
+    window = GTK_WIDGET(mssh_window_new());
+
+    g_signal_connect(G_OBJECT(window), "destroy",
+        G_CALLBACK(on_mssh_destroy), NULL);
+
+	mssh_window_new_session(MSSH_WINDOW(window), env, nhosts, hosts);
+
+    gtk_widget_show_all(window);
+    gtk_main();
+
+	for(i = 0; i < nhosts; i++)
+		free(hosts[i]);
+
+	free(hosts);
+
+    return 0;
+}