#include <limits.h>
#include <locale.h>
#include <poll.h>
+#include <setjmp.h>
+#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
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";
" --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"
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) {
}
int demonize = 0;
int cgi = 0;
- const char *pidfile = NULL;
int verbosity = MSG_DEFAULT;
externalFiles = newHashMap(destroyExternalFileHashEntry, NULL);
HashMap *serviceTable = newHashMap(destroyServiceHashEntry, NULL);
{ "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' },
fatal("Only one pidfile can be given");
}
if (optarg && *optarg) {
- pidfile = strdup(optarg);
+ check(pidfile = strdup(optarg));
}
} else if (!idx--) {
// Certificate
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");
}
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);
}
} 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) {
_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() {
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);
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);
}
'\" t
.\" shellinaboxd.man -- Make command line applications available as AJAX web applications
-.\" Copyright (C) 2008-2009 Markus Gutschke <markus@shellinabox.com>
+.\" Copyright (C) 2008-2010 Markus Gutschke <markus@shellinabox.com>
.\"
.\" 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
.\" 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
[\ \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
The
.B --cgi
option is mutually exclusive with the
-.B --background
+.BR --background ,
+.B --pidfile
and
.B --port
options.
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.
.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