Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

678 lines (572 sloc) 17.781 kB
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* Cherokee
*
* Authors:
* Alvaro Lopez Ortega <alvaro@alobbs.com>
*
* Copyright (C) 2001-2013 Alvaro Lopez Ortega
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include "common-internal.h"
#include <signal.h>
#include <sys/stat.h>
#include "init.h"
#include "server.h"
#include "socket.h"
#include "spawner.h"
#include "config_reader.h"
#include "server-protected.h"
#include "util.h"
#ifdef HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#ifdef HAVE_GETOPT_LONG
# include <getopt.h>
#else
# include "getopt/getopt.h"
#endif
#define APP_NAME \
"Cherokee Web Server: Admin"
#define APP_COPY_NOTICE \
"Written by Alvaro Lopez Ortega <alvaro@alobbs.com>\n\n" \
"Copyright (C) 2001-2013 Alvaro Lopez Ortega.\n" \
"This is free software; see the source for copying conditions. There is NO\n" \
"warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
#define ALPHA_NUM "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
#define PASSWORD_LEN 16
#define DEFAULT_PORT 9090
#define TIMEOUT "25"
#define THREAD_MAX_AUTO 4
#define DEFAULT_DOCUMENTROOT CHEROKEE_DATADIR "/admin"
#define DEFAULT_CONFIG_FILE CHEROKEE_CONFDIR "/cherokee.conf"
#define DEFAULT_UNIX_SOCKET TMPDIR "/cherokee-admin-scgi.socket"
#define DEFAULT_BIND "127.0.0.1"
#define RULE_PRE "vserver!1!rule!"
static int port = DEFAULT_PORT;
static char *document_root = NULL;
static char *config_file = NULL;
static char *bind_to = NULL;
static int debug = 0;
static int unsecure = 0;
static int iocache = 1;
static int scgi_port = 4000;
static int thread_num = -1;
static cherokee_server_t *srv = NULL;
static cherokee_buffer_t password = CHEROKEE_BUF_INIT;
static ret_t
find_empty_port (int starting, int *port)
{
ret_t ret;
cherokee_socket_t s;
int p = starting;
cherokee_buffer_t bind_ = CHEROKEE_BUF_INIT;
cherokee_buffer_add_str (&bind_, "127.0.0.1");
cherokee_socket_init (&s);
while (true) {
cherokee_socket_create_fd (&s, AF_INET);
ret = cherokee_socket_bind (&s, p, &bind_);
cherokee_socket_close (&s);
if (ret == ret_ok)
break;
p += 1;
if (unlikely (p > 0xFFFF)) {
goto error;
}
}
cherokee_socket_mrproper (&s);
cherokee_buffer_mrproper (&bind_);
*port = p;
return ret_ok;
error:
cherokee_socket_close (&s);
cherokee_socket_mrproper (&s);
cherokee_buffer_mrproper (&bind_);
return ret_error;
}
static ret_t
generate_password (cherokee_buffer_t *buf)
{
cuint_t i;
cuint_t n;
for (i=0; i<PASSWORD_LEN; i++) {
n = cherokee_random()%(sizeof(ALPHA_NUM)-1);
cherokee_buffer_add_char (buf, ALPHA_NUM[n]);
}
return ret_ok;
}
static ret_t
remove_old_socket (const char *path)
{
int re;
struct stat info;
/* It might not exist
*/
re = cherokee_stat (path, &info);
if (re != 0) {
return ret_ok;
}
/* If exist, it must be a socket
*/
if (! S_ISSOCK(info.st_mode)) {
PRINT_MSG ("ERROR: Something happens; '%s' isn't a socket.\n", path);
return ret_error;
}
/* Remove it
*/
re = cherokee_unlink (path);
if (re != 0) {
PRINT_MSG ("ERROR: Couldn't remove unix socket '%s'.\n", path);
return ret_error;
}
return ret_ok;
}
static void
print_connection_info (void)
{
printf ("\n");
if (! cherokee_buffer_is_empty (&password)) {
printf ("Login:\n"
" User: admin\n"
" One-time Password: %s\n\n", password.buf);
}
printf ("Web Interface:\n"
" URL: http://%s:%d/\n\n",
(bind_to) ? bind_to : "localhost", port);
fflush(stdout);
}
static ret_t
config_server (cherokee_server_t *srv)
{
ret_t ret;
cherokee_config_node_t conf;
cuint_t nthreads;
cherokee_buffer_t buf = CHEROKEE_BUF_INIT;
cherokee_buffer_t rrd_dir = CHEROKEE_BUF_INIT;
cherokee_buffer_t rrd_bin = CHEROKEE_BUF_INIT;
cherokee_buffer_t fake;
/* Generate the password
*/
if (unsecure == 0) {
ret = generate_password (&password);
if (ret != ret_ok)
return ret;
}
/* Configure the embedded server
*/
if (scgi_port > 0) {
ret = find_empty_port (scgi_port, &scgi_port);
} else {
ret = remove_old_socket (DEFAULT_UNIX_SOCKET);
}
if (ret != ret_ok) {
return ret_error;
}
cherokee_buffer_add_va (&buf, "server!bind!1!port = %d\n", port);
cherokee_buffer_add_str (&buf, "server!ipv6 = 1\n");
cherokee_buffer_add_str (&buf, "server!max_connection_reuse = 0\n");
cherokee_buffer_add_va (&buf, "server!iocache = %d\n", iocache);
if (bind_to) {
cherokee_buffer_add_va (&buf, "server!bind!1!interface = %s\n", bind_to);
}
if (thread_num != -1) {
/* Manually set
*/
cherokee_buffer_add_va (&buf, "server!thread_number = %d\n", thread_num);
} else {
/* Automatically set
*/
nthreads = MIN (cherokee_cpu_number, THREAD_MAX_AUTO);
cherokee_buffer_add_va (&buf, "server!thread_number = %d\n", nthreads);
}
cherokee_buffer_add_str (&buf, "vserver!1!nick = default\n");
cherokee_buffer_add_va (&buf, "vserver!1!document_root = %s\n", document_root);
if (scgi_port <= 0) {
cherokee_buffer_add_va (&buf,
"source!1!nick = app-logic\n"
"source!1!type = interpreter\n"
"source!1!timeout = " TIMEOUT "\n"
"source!1!host = %s\n"
"source!1!interpreter = %s/server.py %s %s %s\n"
"source!1!env_inherited = 1\n",
DEFAULT_UNIX_SOCKET, document_root,
DEFAULT_UNIX_SOCKET, config_file,
(debug) ? "-x" : "");
} else {
cherokee_buffer_add_va (&buf,
"source!1!nick = app-logic\n"
"source!1!type = interpreter\n"
"source!1!timeout = " TIMEOUT "\n"
"source!1!host = localhost:%d\n"
"source!1!interpreter = %s/server.py %d %s %s\n"
"source!1!env_inherited = 1\n",
scgi_port, document_root,
scgi_port, config_file,
(debug) ? "-x" : "");
}
if (debug) {
cherokee_buffer_add_str (&buf, "source!1!debug = 1\n");
}
cherokee_buffer_add_str (&buf,
RULE_PRE "1!match = default\n"
RULE_PRE "1!handler = scgi\n"
RULE_PRE "1!timeout = " TIMEOUT "\n"
RULE_PRE "1!handler!balancer = round_robin\n"
RULE_PRE "1!handler!balancer!source!1 = 1\n");
cherokee_buffer_add_str (&buf, RULE_PRE "1!handler!env!CTK_COOKIE = ");
generate_password (&buf);
cherokee_buffer_add_char (&buf, '\n');
cherokee_buffer_add_str (&buf, RULE_PRE "1!handler!env!CTK_SUBMITTER_SECRET = ");
generate_password (&buf);
cherokee_buffer_add_char (&buf, '\n');
if (! debug) {
cherokee_buffer_add_str (&buf, RULE_PRE "1!encoder!gzip = 1\n");
}
if ((unsecure == 0) &&
(!cherokee_buffer_is_empty (&password)))
{
cherokee_buffer_add_va (&buf,
RULE_PRE "1!auth = authlist\n"
RULE_PRE "1!auth!methods = digest\n"
RULE_PRE "1!auth!realm = Cherokee-admin\n"
RULE_PRE "1!auth!list!1!user = admin\n"
RULE_PRE "1!auth!list!1!password = %s\n",
password.buf);
}
cherokee_buffer_add_str (&buf,
RULE_PRE "2!match = directory\n"
RULE_PRE "2!match!directory = /about\n"
RULE_PRE "2!handler = server_info\n");
cherokee_buffer_add_str (&buf,
RULE_PRE "3!match = directory\n"
RULE_PRE "3!match!directory = /static\n"
RULE_PRE "3!handler = file\n"
RULE_PRE "3!expiration = time\n"
RULE_PRE "3!expiration!time = 30d\n");
cherokee_buffer_add_va (&buf,
RULE_PRE "4!match = request\n"
RULE_PRE "4!match!request = ^/favicon.ico$\n"
RULE_PRE "4!document_root = %s/static/images\n"
RULE_PRE "4!handler = file\n"
RULE_PRE "4!expiration = time\n"
RULE_PRE "4!expiration!time = 30d\n",
document_root);
cherokee_buffer_add_va (&buf,
RULE_PRE "5!match = directory\n"
RULE_PRE "5!match!directory = /icons_local\n"
RULE_PRE "5!handler = file\n"
RULE_PRE "5!document_root = %s\n"
RULE_PRE "5!expiration = time\n"
RULE_PRE "5!expiration!time = 30d\n",
CHEROKEE_ICONSDIR);
cherokee_buffer_add_va (&buf,
RULE_PRE "6!match = directory\n"
RULE_PRE "6!match!directory = /CTK\n"
RULE_PRE "6!handler = file\n"
RULE_PRE "6!document_root = %s/CTK/static\n"
RULE_PRE "6!expiration = time\n"
RULE_PRE "6!expiration!time = 30d\n",
document_root);
/* Embedded help
*/
cherokee_buffer_add_va (&buf,
RULE_PRE "7!match = and\n"
RULE_PRE "7!match!left = directory\n"
RULE_PRE "7!match!left!directory = /help\n"
RULE_PRE "7!match!right = not\n"
RULE_PRE "7!match!right!right = extensions\n"
RULE_PRE "7!match!right!right!extensions = html\n"
RULE_PRE "7!handler = file\n");
cherokee_buffer_add_va (&buf,
RULE_PRE "8!match = fullpath\n"
RULE_PRE "8!match!fullpath!1 = /static/help_404.html\n"
RULE_PRE "8!handler = file\n"
RULE_PRE "8!document_root = %s\n", document_root);
cherokee_buffer_add_va (&buf,
RULE_PRE "9!match = and\n"
RULE_PRE "9!match!left = directory\n"
RULE_PRE "9!match!left!directory = /help\n"
RULE_PRE "9!match!right = not\n"
RULE_PRE "9!match!right!right = exists\n"
RULE_PRE "9!match!right!right!match_any = 1\n"
RULE_PRE "9!handler = redir\n"
RULE_PRE "9!handler!rewrite!1!show = 1\n"
RULE_PRE "9!handler!rewrite!1!substring = /static/help_404.html\n");
cherokee_buffer_add_va (&buf,
RULE_PRE "10!match = directory\n"
RULE_PRE "10!match!directory = /help\n"
RULE_PRE "10!match!final = 0\n"
RULE_PRE "10!document_root = %s\n", CHEROKEE_DOCDIR);
/* GZip
*/
if (! debug) {
cherokee_buffer_add_va (&buf,
RULE_PRE "15!match = extensions\n"
RULE_PRE "15!match!extensions = css,js,html\n"
RULE_PRE "15!match!final = 0\n"
RULE_PRE "15!encoder!gzip = 1\n");
}
/* RRDtool graphs
*/
cherokee_config_node_init (&conf);
cherokee_buffer_fake (&fake, config_file, strlen(config_file));
ret = cherokee_config_reader_parse (&conf, &fake);
if (ret == ret_ok) {
cherokee_config_node_copy (&conf, "server!collector!rrdtool_path", &rrd_bin);
cherokee_config_node_copy (&conf, "server!collector!database_dir", &rrd_dir);
}
if (! cherokee_buffer_is_empty (&rrd_bin)) {
cherokee_buffer_add_va (&buf,
RULE_PRE "20!handler!rrdtool_path = %s\n", rrd_bin.buf);
}
if (! cherokee_buffer_is_empty (&rrd_dir)) {
cherokee_buffer_add_va (&buf,
RULE_PRE "20!handler!database_dir = %s\n", rrd_dir.buf);
}
cherokee_buffer_add_str (&buf,
RULE_PRE "20!match = directory\n"
RULE_PRE "20!match!directory = /graphs\n"
RULE_PRE "20!handler = render_rrd\n"
RULE_PRE "20!expiration = epoch\n"
RULE_PRE "20!expiration!caching = no-cache\n"
RULE_PRE "20!expiration!caching!no-store = 1\n");
cherokee_buffer_add_str (&buf, RULE_PRE "20!document_root = ");
cherokee_buffer_add_buffer (&buf, &cherokee_tmp_dir);
cherokee_buffer_add_va (&buf, "/rrd-cache\n");
/* MIME types
*/
cherokee_buffer_add_str (&buf,
"mime!text/javascript!extensions = js\n"
"mime!text/css!extensions = css\n"
"mime!image/png!extensions = png\n"
"mime!image/jpeg!extensions = jpeg,jpg\n"
"mime!image/svg+xml!extensions = svg,svgz\n"
"mime!image/gif!extensions = gif\n");
ret = cherokee_server_read_config_string (srv, &buf);
if (ret != ret_ok) {
PRINT_ERROR_S ("Could not initialize the server\n");
return ret;
}
cherokee_config_node_mrproper (&conf);
cherokee_buffer_mrproper (&rrd_bin);
cherokee_buffer_mrproper (&rrd_dir);
cherokee_buffer_mrproper (&buf);
return ret_ok;
}
static void
print_help (void)
{
printf (APP_NAME "\n"
"Usage: cherokee-admin [options]\n\n"
" -h, --help Print this help\n"
" -V, --version Print version and exit\n"
" -x, --debug Enables debug\n"
" -u, --unsecure Turn off the authentication\n"
" -b[<IP>], --bind[=<IP>] Bind net iface; no arg means all\n"
" -d<DIR>, --appdir=<DIR> Application directory\n"
" -p<NUM>, --port=<NUM> TCP port\n"
" -t, --internal-unix Use a Unix domain socket internally\n"
" -i, --disable-iocache Disable I/O cache: reduces mem usage\n"
" -T<NUM>, --threads=<NUM> Threads number\n"
" -C<PATH>, --target=<PATH> Configuration file to modify\n\n"
"Report bugs to " PACKAGE_BUGREPORT "\n");
}
static void
process_parameters (int argc, char **argv)
{
ret_t ret;
int c;
cherokee_boolean_t error = false;
struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{"debug", no_argument, NULL, 'x'},
{"unsecure", no_argument, NULL, 'u'},
{"internal-tcp", no_argument, NULL, 't'},
{"disable-iocache", no_argument, NULL, 'i'},
{"bind", optional_argument, NULL, 'b'},
{"appdir", required_argument, NULL, 'd'},
{"port", required_argument, NULL, 'p'},
{"target", required_argument, NULL, 'C'},
{"threads", required_argument, NULL, 'T'},
{NULL, 0, NULL, 0}
};
while ((c = getopt_long(argc, argv, "hVxutib::d:p:C:T:", long_options, NULL)) != -1) {
switch(c) {
case 'b':
free (bind_to);
if (optarg) {
bind_to = strdup(optarg);
} else if (argv[optind] && argv[optind][0] != '-') {
bind_to = strdup(argv[optind]);
optind++;
} else {
bind_to = NULL;
}
break;
case 'p':
ret = cherokee_atoi (optarg, &port);
if (ret != ret_ok) {
error = true;
}
break;
case 'T':
ret = cherokee_atoi (optarg, &thread_num);
if (ret != ret_ok) {
error = true;
}
break;
case 'd':
free (document_root);
document_root = strdup(optarg);
break;
case 'C':
free (config_file);
config_file = strdup(optarg);
break;
case 'x':
debug = 1;
iocache = 0;
break;
case 'u':
unsecure = 1;
break;
case 'i':
iocache = 0;
break;
case 't':
scgi_port = 0;
break;
case 'V':
printf (APP_NAME " " PACKAGE_VERSION "\n" APP_COPY_NOTICE);
exit(0);
case 'h':
case '?':
default:
error = true;
}
if (error) {
print_help();
exit (1);
}
}
/* Check for trailing parameters
*/
for (c = optind; c < argc; c++) {
if ((argv[c] != NULL) && (strlen(argv[c]) > 0)) {
print_help();
exit (1);
}
}
}
static ret_t
check_for_python (void)
{
int re;
pid_t pid;
int exitcode = -1;
char const *args[] = {"env", "python2", "-c", "raise SystemExit", NULL};
pid = fork();
if (pid == -1) {
return ret_error;
} else if (pid == 0) {
execv ("/usr/bin/env", (char * const *) args);
} else {
do {
re = waitpid (pid, &exitcode, 0);
} while (re == -1 && errno == EINTR);
return (WEXITSTATUS(exitcode) == 0)? ret_ok : ret_error;
}
SHOULDNT_HAPPEN;
return ret_error;
}
static void
signals_handler (int sig, siginfo_t *si, void *context)
{
ret_t ret;
int retcode;
UNUSED(context);
switch (sig) {
case SIGCHLD:
ret = cherokee_wait_pid (si->si_pid, &retcode);
break;
case SIGINT:
case SIGTERM:
if (srv->wanna_exit) {
break;
}
printf ("Cherokee-admin is exiting..\n");
cherokee_server_handle_TERM (srv);
break;
default:
PRINT_ERROR ("Unknown signal: %d\n", sig);
}
}
int
main (int argc, char **argv)
{
ret_t ret;
struct sigaction act;
/* Globals */
document_root = strdup (DEFAULT_DOCUMENTROOT);
config_file = strdup (DEFAULT_CONFIG_FILE);
bind_to = strdup (DEFAULT_BIND);
if ((!bind_to) || (!config_file) || (!document_root)) {
PRINT_MSG ("ERROR: Couldn't allocate memory.\n");
exit (EXIT_ERROR);
}
/* Python */
ret = check_for_python();
if (ret != ret_ok) {
PRINT_MSG ("ERROR: Couldn't find python.\n");
exit (EXIT_ERROR);
}
/* Signal handling */
act.sa_handler = SIG_IGN;
sigaction (SIGPIPE, &act, NULL);
memset(&act, 0, sizeof(act));
act.sa_sigaction = signals_handler;
act.sa_flags = SA_SIGINFO;
sigaction (SIGCHLD, &act, NULL);
sigaction (SIGINT, &act, NULL);
sigaction (SIGTERM, &act, NULL);
/* Initialize the embedded server */
cherokee_init();
/* Seed random numbers
*/
cherokee_random_seed();
cherokee_spawner_set_active (false);
process_parameters (argc, argv);
ret = cherokee_server_new (&srv);
if (ret != ret_ok)
exit (EXIT_ERROR);
ret = config_server (srv);
if (ret != ret_ok)
exit (EXIT_ERROR);
ret = cherokee_server_initialize (srv);
if (ret != ret_ok)
exit (EXIT_ERROR);
print_connection_info();
ret = cherokee_server_unlock_threads (srv);
if (ret != ret_ok)
exit (EXIT_ERROR);
do {
ret = cherokee_server_step (srv);
} while (ret == ret_eagain);
cherokee_server_stop (srv);
cherokee_server_free (srv);
cherokee_mrproper();
return EXIT_OK;
}
Jump to Line
Something went wrong with that request. Please try again.