Allow retrieval of the user's default login shell from /etc/passwd.

Allow the code to be built without support for the LOGIN service, as
calling /bin/login does not work well on Fedora.


git-svn-id: https://shellinabox.googlecode.com/svn/trunk@198 0da03de8-d603-11dd-86c2-0f8696b7b6f9
This commit is contained in:
zodiac@gmail.com 2009-12-04 06:33:36 +00:00
parent 5caa8b7288
commit 0fcf50735e
16 changed files with 199 additions and 57 deletions

View file

@ -1,3 +1,11 @@
2009-12-03 Markus Gutschke <markus@shellinabox.com>
* Allow retrieval of the user's default login shell from
/etc/passwd.
* Allow the code to be built without support for the LOGIN
service, as calling /bin/login does not work well on Fedora.
2009-12-02 Markus Gutschke <markus@shellinabox.com>
* Added fallback code that dynamically computes the correct commandline

View file

@ -196,12 +196,18 @@ ${top_srcdir}/demo/vt100.js: ${top_srcdir}/shellinabox/vt100.js
shellinaboxd.1: shellinabox/shellinaboxd.man.in config.h
@src="${top_srcdir}/shellinabox/shellinaboxd.man.in"; \
echo preprocess "$$src" '>'"$@"; \
if sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \
grep 'HAVE_BIN_LOGIN' >/dev/null 2>&1; then \
sed -e '/^#ifndef *HAVE_BIN_LOGIN$$/,/^#endif$$/d' "$$src"; \
else \
sed -e '/^#ifdef *HAVE_BIN_LOGIN$$/,/^#endif$$/d' "$$src"; \
fi | \
if [ `sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \
egrep 'HAVE_OPENSSL_BIO_H|HAVE_OPENSSL_ERR_H|HAVE_OPENSSL_SSL_H'|\
wc -l` -eq 3 ]; then \
sed -e '/^#ifndef *HAVE_OPENSSL$$/,/^#endif$$/d' "$$src"; \
sed -e '/^#ifndef *HAVE_OPENSSL$$/,/^#endif$$/d'; \
else \
sed -e '/^#ifdef *HAVE_OPENSSL$$/,/^#endif$$/d' "$$src"; \
sed -e '/^#ifdef *HAVE_OPENSSL$$/,/^#endif$$/d'; \
fi | \
if sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \
grep 'HAVE_SECURITY_PAM_APPL_H' >/dev/null 2>&1; then \

View file

@ -1209,12 +1209,18 @@ ${top_srcdir}/demo/vt100.js: ${top_srcdir}/shellinabox/vt100.js
shellinaboxd.1: shellinabox/shellinaboxd.man.in config.h
@src="${top_srcdir}/shellinabox/shellinaboxd.man.in"; \
echo preprocess "$$src" '>'"$@"; \
if sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \
grep 'HAVE_BIN_LOGIN' >/dev/null 2>&1; then \
sed -e '/^#ifndef *HAVE_BIN_LOGIN$$/,/^#endif$$/d' "$$src"; \
else \
sed -e '/^#ifdef *HAVE_BIN_LOGIN$$/,/^#endif$$/d' "$$src"; \
fi | \
if [ `sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \
egrep 'HAVE_OPENSSL_BIO_H|HAVE_OPENSSL_ERR_H|HAVE_OPENSSL_SSL_H'|\
wc -l` -eq 3 ]; then \
sed -e '/^#ifndef *HAVE_OPENSSL$$/,/^#endif$$/d' "$$src"; \
sed -e '/^#ifndef *HAVE_OPENSSL$$/,/^#endif$$/d'; \
else \
sed -e '/^#ifdef *HAVE_OPENSSL$$/,/^#endif$$/d' "$$src"; \
sed -e '/^#ifdef *HAVE_OPENSSL$$/,/^#endif$$/d'; \
fi | \
if sed -e 's/^#define \([^ ]*\).*/\1/' -e t -e d config.h | \
grep 'HAVE_SECURITY_PAM_APPL_H' >/dev/null 2>&1; then \

View file

@ -11,11 +11,16 @@ some tips on getting things working:
you start the daemon as "root".
To fix this problem, consider explicitly specifying a service definition.
One of these two should work:
--service /:AUTH:HOME:/bin/bash
--service /:AUTH:HOME:SHELL
or
--service /:SSH
The latter requires that you have a locally running "sshd" daemon.
- Alternatively, consider running "./configure --disable-login" before building
the daemon. This will completely remove support for the "LOGIN" service, and
shellinaboxd will instead use a default "SSH" service for both unprivileged
and for "root" users.
- On Fedora, PAM authentication does not work for shellinabox until you
explicitly configure it. This means, using "AUTH" in the service definition
will not allow you to log in.

View file

@ -1,11 +1,14 @@
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Set if you want support for calling /bin/login */
#define HAVE_BIN_LOGIN 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the `dlopen' function. */
/* #undef HAVE_DLOPEN */
#define HAVE_DLOPEN 1
/* Define to 1 if you have the `getgrgid_r' function. */
#define HAVE_GETGRGID_R 1
@ -138,10 +141,10 @@
#define STDC_HEADERS 1
/* Most recent revision number in the version control system */
#define VCS_REVISION "197"
#define VCS_REVISION "198"
/* Version number of package */
#define VERSION "2.10"
/* Define to empty if `const' does not conform to ANSI C. */
#define const /**/
/* #undef const */

View file

@ -1,5 +1,8 @@
/* config.h.in. Generated from configure.ac by autoheader. */
/* Set if you want support for calling /bin/login */
#undef HAVE_BIN_LOGIN
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H

47
configure vendored
View file

@ -861,6 +861,7 @@ with_pic
enable_fast_install
with_gnu_ld
enable_libtool_lock
enable_login
enable_ssl
enable_pam
enable_runtime_loading
@ -1500,6 +1501,11 @@ Optional Features:
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
--disable-login on some systems (e.g. Fedora), calling /bin/login
does not work well. If you know that your system
suffers from this problem, set this option to
remove support for the LOGIN keyword in the
service description.
--disable-ssl if available at built-time, support for SSL
connections will be enabled. It can still be
disabled at run-time, either on the daemon's
@ -2319,7 +2325,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $
ac_compiler_gnu=$ac_cv_c_compiler_gnu
VCS_REVISION=197
VCS_REVISION=198
cat >>confdefs.h <<_ACEOF
@ -4470,13 +4476,13 @@ if test "${lt_cv_nm_interface+set}" = set; then :
else
lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:4473: $ac_compile\"" >&5)
(eval echo "\"\$as_me:4479: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
cat conftest.err >&5
(eval echo "\"\$as_me:4476: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval echo "\"\$as_me:4482: $NM \\\"conftest.$ac_objext\\\"\"" >&5)
(eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out)
cat conftest.err >&5
(eval echo "\"\$as_me:4479: output\"" >&5)
(eval echo "\"\$as_me:4485: output\"" >&5)
cat conftest.out >&5
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
@ -5682,7 +5688,7 @@ ia64-*-hpux*)
;;
*-*-irix6*)
# Find out which ABI we are using.
echo '#line 5685 "configure"' > conftest.$ac_ext
echo '#line 5691 "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
@ -7208,11 +7214,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7211: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7217: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:7215: \$? = $ac_status" >&5
echo "$as_me:7221: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -7547,11 +7553,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7550: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7556: $lt_compile\"" >&5)
(eval "$lt_compile" 2>conftest.err)
ac_status=$?
cat conftest.err >&5
echo "$as_me:7554: \$? = $ac_status" >&5
echo "$as_me:7560: \$? = $ac_status" >&5
if (exit $ac_status) && test -s "$ac_outfile"; then
# The compiler can only warn and ignore the option if not recognized
# So say no if there are warnings other than the usual output.
@ -7652,11 +7658,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7655: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7661: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7659: \$? = $ac_status" >&5
echo "$as_me:7665: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -7707,11 +7713,11 @@ else
-e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
-e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
-e 's:$: $lt_compiler_flag:'`
(eval echo "\"\$as_me:7710: $lt_compile\"" >&5)
(eval echo "\"\$as_me:7716: $lt_compile\"" >&5)
(eval "$lt_compile" 2>out/conftest.err)
ac_status=$?
cat out/conftest.err >&5
echo "$as_me:7714: \$? = $ac_status" >&5
echo "$as_me:7720: \$? = $ac_status" >&5
if (exit $ac_status) && test -s out/conftest2.$ac_objext
then
# The compiler can only warn and ignore the option if not recognized
@ -10090,7 +10096,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 10093 "configure"
#line 10099 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -10186,7 +10192,7 @@ else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
lt_status=$lt_dlunknown
cat > conftest.$ac_ext <<_LT_EOF
#line 10189 "configure"
#line 10195 "configure"
#include "confdefs.h"
#if HAVE_DLFCN_H
@ -10680,6 +10686,17 @@ fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
# Check whether --enable-login was given.
if test "${enable_login+set}" = set; then :
enableval=$enable_login;
fi
if test "x$enable_login" != xno; then
$as_echo "#define HAVE_BIN_LOGIN 1" >>confdefs.h
fi
# Check whether --enable-ssl was given.
if test "${enable_ssl+set}" = set; then :
enableval=$enable_ssl;

View file

@ -2,7 +2,7 @@ AC_PREREQ(2.57)
dnl This is the one location where the authoritative version number is stored
AC_INIT(shellinabox, 2.10, markus@shellinabox.com)
VCS_REVISION=197
VCS_REVISION=198
AC_SUBST(VCS_REVISION)
AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}",
[Most recent revision number in the version control system])
@ -55,6 +55,19 @@ AC_TRY_LINK([#include <pthread.h>
[AC_DEFINE(HAVE_SIGWAIT, 1,
Define to 1 if you have a working sigwait)])
dnl On some systems, calling /bin/login does not work. Disable the LOGIN
dnl feature, if the user tells us that it does not do the right thing.
AC_ARG_ENABLE(login,
[ --disable-login on some systems (e.g. Fedora), calling /bin/login
does not work well. If you know that your system
suffers from this problem, set this option to
remove support for the LOGIN keyword in the
service description.])
if test "x$enable_login" != xno; then
AC_DEFINE(HAVE_BIN_LOGIN, 1,
Set if you want support for calling /bin/login)
fi
dnl We automatically detect SSL support, but allow users to disable it
AC_ARG_ENABLE(ssl,
[ --disable-ssl if available at built-time, support for SSL

View file

@ -1955,7 +1955,7 @@ VT100.prototype.toggleBell = function() {
};
VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + "2.10 (revision 197)" +
alert("VT100 Terminal Emulator " + "2.10 (revision 198)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" +
"For more information check http://shellinabox.com");
};

View file

@ -727,29 +727,34 @@ static const struct passwd *getPWEnt(uid_t uid) {
struct passwd pwbuf, *pw;
char *buf;
#ifdef _SC_GETPW_R_SIZE_MAX
int len = sysconf(_SC_GETPW_R_SIZE_MAX);
int len = sysconf(_SC_GETPW_R_SIZE_MAX);
if (len <= 0) {
len = 4096;
len = 4096;
}
#else
int len = 4096;
int len = 4096;
#endif
check(buf = malloc(len));
check(buf = malloc(len));
check(!getpwuid_r(uid, &pwbuf, buf, len, &pw) && pw);
if (!pw->pw_name ) pw->pw_name = "";
if (!pw->pw_passwd) pw->pw_passwd = "";
if (!pw->pw_gecos ) pw->pw_gecos = "";
if (!pw->pw_dir ) pw->pw_dir = "";
if (!pw->pw_shell ) pw->pw_shell = "";
struct passwd *passwd;
check(passwd = calloc(sizeof(struct passwd) +
strlen(pw->pw_name) +
strlen(pw->pw_passwd) +
strlen(pw->pw_gecos) +
strlen(pw->pw_dir) +
strlen(pw->pw_shell) + 5, 1));
passwd->pw_uid = pw->pw_uid;
passwd->pw_gid = pw->pw_gid;
strncat(passwd->pw_shell = strrchr(
strncat(passwd->pw_dir = strrchr(
strncat(passwd->pw_gecos = strrchr(
strncat(passwd->pw_passwd = strrchr(
strncat(passwd->pw_name = (char *)(passwd + 1),
check(passwd = calloc(sizeof(struct passwd) +
strlen(pw->pw_name) +
strlen(pw->pw_passwd) +
strlen(pw->pw_gecos) +
strlen(pw->pw_dir) +
strlen(pw->pw_shell) + 5, 1));
passwd->pw_uid = pw->pw_uid;
passwd->pw_gid = pw->pw_gid;
strncat(passwd->pw_shell = strrchr(
strncat(passwd->pw_dir = strrchr(
strncat(passwd->pw_gecos = strrchr(
strncat(passwd->pw_passwd = strrchr(
strncat(passwd->pw_name = (char *)(passwd + 1),
pw->pw_name, strlen(pw->pw_name)), '\000') + 1,
pw->pw_passwd, strlen(pw->pw_passwd)), '\000') + 1,
pw->pw_gecos, strlen(pw->pw_gecos)), '\000') + 1,
@ -946,6 +951,12 @@ static pam_handle_t *internalLogin(struct Service *service, struct Utmp *utmp,
free((void *)fqdn);
free((void *)hostname);
if (service->useDefaultShell) {
check(!service->cmdline);
service->cmdline = strdup(*pw->pw_shell ?
pw->pw_shell : "/bin/sh");
}
if (restricted &&
(service->uid != restricted || service->gid != pw->pw_gid)) {
puts("\nAccess denied!");
@ -1226,8 +1237,19 @@ static void execService(int width, int height, struct Service *service,
extern char **environ;
environ = environment;
char *cmd = strrchr(argv[0], '/');
execvp(cmd ? cmd + 1: argv[0], argv);
char *cmd = strdup(argv[0]);
char *slash = strrchr(argv[0], '/');
if (slash) {
memmove(argv[0], slash + 1, strlen(slash));
}
if (service->useDefaultShell) {
int len = strlen(argv[0]);
check(argv[0] = realloc(argv[0], len + 2));
memmove(argv[0] + 1, argv[0], len);
argv[0][0] = '-';
argv[0][len + 1] = '\000';
}
execvp(cmd, argv);
}
void setWindowSize(int pty, int width, int height) {

View file

@ -83,6 +83,7 @@ void initService(struct Service *service, const char *arg) {
}
arg = ptr + 1;
#ifdef HAVE_BIN_LOGIN
// The next part of the argument is either the word 'LOGIN' or the
// application definition.
if (!strcmp(arg, "LOGIN")) {
@ -93,6 +94,7 @@ void initService(struct Service *service, const char *arg) {
service->useLogin = 1;
service->useHomeDir = 0;
service->authUser = 0;
service->useDefaultShell = 0;
service->uid = 0;
service->gid = 0;
check(service->user = strdup("root"));
@ -100,10 +102,13 @@ void initService(struct Service *service, const char *arg) {
check(service->cwd = strdup("/"));
check(service->cmdline = strdup(
"/bin/login -p -h ${peer}"));
} else if (!strcmp(arg, "SSH") || !strncmp(arg, "SSH:", 4)) {
} else
#endif
if (!strcmp(arg, "SSH") || !strncmp(arg, "SSH:", 4)) {
service->useLogin = 0;
service->useHomeDir = 0;
service->authUser = 2;
service->useDefaultShell = 0;
service->uid = -1;
service->gid = -1;
service->user = NULL;
@ -204,7 +209,13 @@ void initService(struct Service *service, const char *arg) {
if (!*arg) {
goto error;
}
check(service->cmdline = strdup(arg));
if (!strcmp(arg, "SHELL")) {
service->useDefaultShell = 1;
service->cmdline = NULL;
} else {
service->useDefaultShell = 0;
check(service->cmdline = strdup(arg));
}
}
free(desc);
}

View file

@ -54,6 +54,7 @@ struct Service {
int useLogin;
int useHomeDir;
int authUser;
int useDefaultShell;
int uid;
int gid;
const char *user;

View file

@ -358,7 +358,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) {
};
ShellInABox.prototype.about = function() {
alert("Shell In A Box version " + "2.10 (revision 197)" +
alert("Shell In A Box version " + "2.10 (revision 198)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" +
"For more information check http://shellinabox.com" +
(typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ?

View file

@ -765,10 +765,15 @@ static void usage(void) {
"be made available\n"
"through the web interface:\n"
" SERVICE := <url-path> ':' APP\n"
" APP := 'LOGIN' | 'SSH' [ : <host> ] | "
"USER ':' CWD ':' <cmdline>\n"
" APP := "
#ifdef HAVE_BIN_LOGIN
"'LOGIN' | "
#endif
"'SSH' [ : <host> ] | "
"USER ':' CWD ':' CMD\n"
" USER := %s<username> ':' <groupname>\n"
" CWD := 'HOME' | <dir>\n"
" CMD := 'SHELL' | <cmdline>\n"
"\n"
"<cmdline> supports variable expansion:\n"
" ${columns} - number of columns\n"
@ -1096,8 +1101,14 @@ static void parseArgs(int argc, char * const argv[]) {
// If the user did not register any services, provide the default service
if (!getHashmapSize(serviceTable)) {
addToHashMap(serviceTable, "/", (char *)newService(geteuid() ? ":SSH" :
":LOGIN"));
addToHashMap(serviceTable, "/",
(char *)newService(
#ifdef HAVE_BIN_LOGIN
geteuid() ? ":SSH" : ":LOGIN"
#else
":SSH"
#endif
));
}
enumerateServices(serviceTable);
deleteHashMap(serviceTable);

View file

@ -44,7 +44,7 @@
.\" The most up-to-date version of this program is always available from
.\" http://shellinabox.com
.\"
.TH SHELLINABOXD 1 "Nov 21, 2009"
.TH SHELLINABOXD 1 "Dec 03, 2009"
.SH NAME
shellinaboxd \- publish command line shell through AJAX interface
.SH SYNOPSIS
@ -330,6 +330,7 @@ One or more services can be registered on different URL paths:
\fISERVICE\fP := <url-path> ':' \fIAPPLICATION\fP
.in
#ifdef HAVE_BIN_LOGIN
There is a pre-defined \fIapplication\fP, 'LOGIN', which causes the
daemon to invoke
.B /bin/login
@ -343,12 +344,34 @@ was defined. Starting
requires
.B root
privileges.
#endif
There is another pre-defined \fIapplication\fP, 'SSH'. Instead of invoking
There is
#ifdef HAVE_BIN_LOGIN
another
#endif
#ifndef HAVE_BIN_LOGIN
a
#endif
pre-defined \fIapplication\fP, 'SSH'.
#ifdef HAVE_BIN_LOGIN
Instead of invoking
.BR /bin/login ,
it calls
it
#endif
#ifndef HAVE_BIN_LOGIN
It
#endif
calls
.BR ssh .
This is the default option for unprivileged users, if no
This is the default
#ifdef HAVE_BIN_LOGIN
option for unprivileged users,
#endif
#ifndef HAVE_BIN_LOGIN
option,
#endif
if no
.B --service
was defined. This operation is available to both privileged and regular
users. If the optional \fIhost\fP parameter is omitted,
@ -359,7 +382,12 @@ connects to
Alternatively, an \fIapplication\fP can be specified by providing a
\fIuser\fP description, a working directory, and a command line:
.in +4
\fIAPPLICATION\fP := 'LOGIN' | 'SSH' [ ':' <host> ] | \fIUSER\fP ':' \fICWD\fP ':' <cmdline>
#ifdef HAVE_BIN_LOGIN
\fIAPPLICATION\fP := 'LOGIN' | 'SSH' [ ':' <host> ] | \fIUSER\fP ':' \fICWD\fP ':' \fICMD\fP
#endif
#ifndef HAVE_BIN_LOGIN
\fIAPPLICATION\fP := 'SSH' [ ':' <host> ] | \fIUSER\fP ':' \fICWD\fP ':' \fICMD\fP
#endif
#ifdef HAVE_PAM
.in
@ -383,6 +411,14 @@ can be the user's home directory:
\fICWD\fP := 'HOME' : <dir>
.in
The command that
.B shellinaboxd
executes can either be specified as the 'SHELL' keyword, denoting the user's
default login shell, or an arbitrary command line:
.in +4
\fICMD\fP := 'SHELL' : <cmdline>
.in
The <cmdline> supports expansion of variables of the form ${VAR}.
Supported variables are:
.RS

View file

@ -1955,7 +1955,7 @@ VT100.prototype.toggleBell = function() {
};
VT100.prototype.about = function() {
alert("VT100 Terminal Emulator " + "2.10 (revision 197)" +
alert("VT100 Terminal Emulator " + "2.10 (revision 198)" +
"\nCopyright 2008-2009 by Markus Gutschke\n" +
"For more information check http://shellinabox.com");
};