diff --git a/ChangeLog b/ChangeLog index 2e216b0..c1c14bd 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-09-11 Markus Gutschke + + * Added --pidfile= option that can be used even if running in the + foreground. + 2010-09-04 Markus Gutschke * Added an optional on-screen keyboard. Must be activated by the diff --git a/config.h b/config.h index 79359d1..14cbbcc 100644 --- a/config.h +++ b/config.h @@ -153,7 +153,7 @@ #define STDC_HEADERS 1 /* Most recent revision number in the version control system */ -#define VCS_REVISION "222" +#define VCS_REVISION "223" /* Version number of package */ #define VERSION "2.10" diff --git a/configure b/configure index 5b7e0e7..118c49a 100755 --- a/configure +++ b/configure @@ -2328,7 +2328,7 @@ ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $ ac_compiler_gnu=$ac_cv_c_compiler_gnu -VCS_REVISION=222 +VCS_REVISION=223 cat >>confdefs.h <<_ACEOF diff --git a/configure.ac b/configure.ac index 1aad875..ecd2bf9 100644 --- a/configure.ac +++ b/configure.ac @@ -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=222 +VCS_REVISION=223 AC_SUBST(VCS_REVISION) AC_DEFINE_UNQUOTED(VCS_REVISION, "${VCS_REVISION}", [Most recent revision number in the version control system]) diff --git a/demo/vt100.js b/demo/vt100.js index e070669..a36b768 100644 --- a/demo/vt100.js +++ b/demo/vt100.js @@ -2384,7 +2384,7 @@ VT100.prototype.toggleCursorBlinking = function() { }; VT100.prototype.about = function() { - alert("VT100 Terminal Emulator " + "2.10 (revision 222)" + + alert("VT100 Terminal Emulator " + "2.10 (revision 223)" + "\nCopyright 2008-2010 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); }; diff --git a/shellinabox/shell_in_a_box.js b/shellinabox/shell_in_a_box.js index 7fdda06..033127d 100644 --- a/shellinabox/shell_in_a_box.js +++ b/shellinabox/shell_in_a_box.js @@ -358,7 +358,7 @@ ShellInABox.prototype.extendContextMenu = function(entries, actions) { }; ShellInABox.prototype.about = function() { - alert("Shell In A Box version " + "2.10 (revision 222)" + + alert("Shell In A Box version " + "2.10 (revision 223)" + "\nCopyright 2008-2010 by Markus Gutschke\n" + "For more information check http://shellinabox.com" + (typeof serverSupportsSSL != 'undefined' && serverSupportsSSL ? diff --git a/shellinabox/shellinaboxd.c b/shellinabox/shellinaboxd.c index c432ffe..e80d2fe 100644 --- a/shellinabox/shellinaboxd.c +++ b/shellinabox/shellinaboxd.c @@ -52,6 +52,8 @@ #include #include #include +#include +#include #include #include #include @@ -94,6 +96,9 @@ static char *cgiSessionKey; static int cgiSessions; static char *cssStyleSheet; static struct UserCSS *userCSSList; +static const char *pidfile; +static sigjmp_buf jmpenv; +static volatile int exiting; static char *jsonEscape(const char *buf, int len) { static const char *hexDigit = "0123456789ABCDEF"; @@ -757,6 +762,7 @@ static void usage(void) { " --localhost-only only listen on 127.0.0.1\n" " --no-beep suppress all audio output\n" " -n, --numeric do not resolve hostnames\n" + " --pidfile=PIDFILE publish pid of daemon process\n" " -p, --port=PORT select a port (default: %d)\n" " -s, --service=SERVICE define one or more services\n" "%s" @@ -822,6 +828,13 @@ static void destroyExternalFileHashEntry(void *arg, char *key, char *value) { free(value); } +static void sigHandler(int signo, siginfo_t *info, void *context) { + if (exiting++) { + _exit(1); + } + siglongjmp(jmpenv, 1); +} + static void parseArgs(int argc, char * const argv[]) { int hasSSL = serverSupportsSSL(); if (!hasSSL) { @@ -829,7 +842,6 @@ static void parseArgs(int argc, char * const argv[]) { } int demonize = 0; int cgi = 0; - const char *pidfile = NULL; int verbosity = MSG_DEFAULT; externalFiles = newHashMap(destroyExternalFileHashEntry, NULL); HashMap *serviceTable = newHashMap(destroyServiceHashEntry, NULL); @@ -855,6 +867,7 @@ static void parseArgs(int argc, char * const argv[]) { { "localhost-only", 0, 0, 0 }, { "no-beep", 0, 0, 0 }, { "numeric", 0, 0, 'n' }, + { "pidfile", 1, 0, 0 }, { "port", 1, 0, 'p' }, { "service", 1, 0, 's' }, { "disable-ssl", 0, 0, 't' }, @@ -894,7 +907,7 @@ static void parseArgs(int argc, char * const argv[]) { fatal("Only one pidfile can be given"); } if (optarg && *optarg) { - pidfile = strdup(optarg); + check(pidfile = strdup(optarg)); } } else if (!idx--) { // Certificate @@ -957,6 +970,9 @@ static void parseArgs(int argc, char * const argv[]) { if (demonize) { fatal("CGI and background operations are mutually exclusive"); } + if (pidfile) { + fatal("CGI operation and --pidfile= are mutually exclusive"); + } if (port) { fatal("Cannot specify a port for CGI operation"); } @@ -987,7 +1003,7 @@ static void parseArgs(int argc, char * const argv[]) { check(path = malloc(ptr - optarg + 1)); memcpy(path, optarg, ptr - optarg); path[ptr - optarg] = '\000'; - file = strdup(ptr + 1); + check(file = strdup(ptr + 1)); if (getRefFromHashMap(externalFiles, path)) { fatal("Duplicate static-file definition for \"%s\".", path); } @@ -1022,6 +1038,18 @@ static void parseArgs(int argc, char * const argv[]) { } else if (!idx--) { // Numeric numericHosts = 1; + } else if (!idx--) { + // Pidfile + if (cgi) { + fatal("CGI operation and --pidfile= are mutually exclusive"); + } + if (!optarg || !*optarg) { + fatal("Must specify a filename for --pidfile= option"); + } + if (pidfile) { + fatal("Only one pidfile can be given"); + } + check(pidfile = strdup(optarg)); } else if (!idx--) { // Port if (port) { @@ -1138,21 +1166,23 @@ static void parseArgs(int argc, char * const argv[]) { _exit(0); } setsid(); - if (pidfile) { + } + if (pidfile) { #ifndef O_LARGEFILE #define O_LARGEFILE 0 #endif - int fd = NOINTR(open(pidfile, + int fd = NOINTR(open(pidfile, O_WRONLY|O_TRUNC|O_LARGEFILE|O_CREAT, 0644)); - if (fd >= 0) { - char buf[40]; - NOINTR(write(fd, buf, snprintf(buf, 40, "%d", (int)getpid()))); - check(!NOINTR(close(fd))); - } + if (fd >= 0) { + char buf[40]; + NOINTR(write(fd, buf, snprintf(buf, 40, "%d", (int)getpid()))); + check(!NOINTR(close(fd))); + } else { + free((char *)pidfile); + pidfile = NULL; } } - free((char *)pidfile); } static void removeLimits() { @@ -1278,7 +1308,20 @@ int main(int argc, char * const argv[]) { iterateOverHashMap(externalFiles, registerExternalFiles, server); // Start the server - serverLoop(server); + if (!sigsetjmp(jmpenv, 1)) { + // Clean up upon orderly shut down. Do _not_ cleanup if we die + // unexpectedly, as we cannot guarantee if we are still in a valid + // static. This means, we should never catch SIGABRT. + static const int signals[] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM }; + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = sigHandler; + sa.sa_flags = SA_SIGINFO | SA_RESETHAND; + for (int i = 0; i < sizeof(signals)/sizeof(*signals); ++i) { + sigaction(signals[i], &sa, NULL); + } + serverLoop(server); + } // Clean up deleteServer(server); @@ -1290,6 +1333,27 @@ int main(int argc, char * const argv[]) { free(services); free(certificateDir); free(cgiSessionKey); + if (pidfile) { + // As a convenience, remove the pidfile, if it is still the version that + // we wrote. In general, pidfiles are not expected to be incredibly + // reliable, as there is no way to properly deal with multiple programs + // accessing the same pidfile. But we at least make a best effort to be + // good citizens. + char buf[40]; + int fd = open(pidfile, O_RDONLY); + if (fd >= 0) { + ssize_t sz; + NOINTR(sz = read(fd, buf, sizeof(buf)-1)); + NOINTR(close(fd)); + if (sz > 0) { + buf[sz] = '\000'; + if (atoi(buf) == getpid()) { + unlink(pidfile); + } + } + } + free((char *)pidfile); + } info("Done"); _exit(0); } diff --git a/shellinabox/shellinaboxd.man.in b/shellinabox/shellinaboxd.man.in index 9dbe40a..c617bbf 100644 --- a/shellinabox/shellinaboxd.man.in +++ b/shellinabox/shellinaboxd.man.in @@ -1,6 +1,6 @@ '\" t .\" shellinaboxd.man -- Make command line applications available as AJAX web applications -.\" Copyright (C) 2008-2009 Markus Gutschke +.\" Copyright (C) 2008-2010 Markus Gutschke .\" .\" This program is free software; you can redistribute it and/or modify .\" it under the terms of the GNU General Public License version 2 as @@ -44,7 +44,7 @@ .\" The most up-to-date version of this program is always available from .\" http://shellinabox.com .\" -.TH SHELLINABOXD 1 "Dec 03, 2009" +.TH SHELLINABOXD 1 "Sep 11, 2010" .SH NAME shellinaboxd \- publish command line shell through AJAX interface .SH SYNOPSIS @@ -65,6 +65,7 @@ shellinaboxd \- publish command line shell through AJAX interface [\ \fB--localhost-only\fP\ ] [\ \fB--no-beep\fP\ ] [\ \fB-n\fP\ | \fB--numeric\fP\ ] +[\ \fB--pidfile=\fP\fIpidfile\fP\ ] [\ \fB-p\fP\ | \fB--port=\fP\fIport\fP\ ] [\ \fB-s\fP\ | \fB--service=\fP\fIservice\fP\ ] #ifdef HAVE_OPENSSL @@ -188,7 +189,8 @@ should be configured to pass through the firewall. The .B --cgi option is mutually exclusive with the -.B --background +.BR --background , +.B --pidfile and .B --port options. @@ -312,6 +314,12 @@ By default, host names of peers get resolved before logging them. As DNS look-ups can be expensive, it is possible to request logging of numeric IP addresses, instead. .TP +\fB--pidfile=\fP\fIpidfile\fP +The +.B shellinaboxd +daemon can be configured to store its process identifier in +.IR pidfile . +.TP \fB-p\fP\ |\ \fB--port=\fP\fIport\fP Unless overridden by this option, the web server listens on port 4200 for incoming HTTP and HTTPS requests. @@ -798,7 +806,7 @@ and .B --user options should be used to change to a dedicated user. .SH AUTHOR -Copyright (C) 2008-2009 by Markus Gutschke +Copyright (C) 2008-2010 by Markus Gutschke .RI < "markus@shellinabox.com" >. .P This program is free software; you can redistribute it and/or modify diff --git a/shellinabox/vt100.js b/shellinabox/vt100.js index e070669..a36b768 100644 --- a/shellinabox/vt100.js +++ b/shellinabox/vt100.js @@ -2384,7 +2384,7 @@ VT100.prototype.toggleCursorBlinking = function() { }; VT100.prototype.about = function() { - alert("VT100 Terminal Emulator " + "2.10 (revision 222)" + + alert("VT100 Terminal Emulator " + "2.10 (revision 223)" + "\nCopyright 2008-2010 by Markus Gutschke\n" + "For more information check http://shellinabox.com"); };