tinyproxy/src/tinyproxy.c

411 lines
9.8 KiB
C
Raw Normal View History

/* $Id: tinyproxy.c,v 1.27 2002-04-18 17:59:21 rjkaes Exp $
*
* The initialise routine. Basically sets up all the initial stuff (logfile,
* listening socket, config options, etc.) and then sits there and loops
* over the new connections until the daemon is closed. Also has additional
* functions to handle the "user friendly" aspects of a program (usage,
* stats, etc.) Like any good program, most of the work is actually done
* elsewhere.
*
* Copyright (C) 1998 Steven Young
* Copyright (C) 1999 Robert James Kaes (rjkaes@flarenet.com)
* Copyright (C) 2000 Chris Lightfoot (chris@ex-parrot.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2, or (at your option) any
* later version.
*
* 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.
*/
#include "tinyproxy.h"
#include "anonymous.h"
#include "buffer.h"
#include "filter.h"
#include "log.h"
#include "reqs.h"
#include "sock.h"
#include "stats.h"
#include "thread.h"
#include "utils.h"
void takesig(int sig);
extern int yyparse(void);
extern FILE *yyin;
/*
* Global Structures
*/
struct config_s config;
float load = 0.00;
bool_t log_rotation_request = FALSE;
/*
* Handle a signal
*/
2001-11-22 08:31:10 +08:00
void
takesig(int sig)
{
switch (sig) {
case SIGHUP:
log_rotation_request = TRUE;
break;
case SIGTERM:
config.quit = TRUE;
break;
}
if (sig != SIGTERM)
signal(sig, takesig);
signal(SIGPIPE, SIG_IGN);
}
/*
* Display the version information for the user.
*/
2001-11-22 08:31:10 +08:00
static void
display_version(void)
{
printf("%s %s (%s)\n", PACKAGE, VERSION, TARGET_SYSTEM);
}
/*
* Display the copyright and license for this program.
*/
2001-11-22 08:31:10 +08:00
static void
display_license(void)
{
display_version();
printf("\
Copyright 1998 Steven Young (sdyoung@well.com)\n\
2002-04-08 05:36:39 +08:00
Copyright 1998-2002 Robert James Kaes (rjkaes@users.sourceforge.net)\n\
Copyright 1999 George Talusan (gstalusan@uwaterloo.ca)\n\
Copyright 2000 Chris Lightfoot (chris@ex-parrot.com)\n\
\n\
This program is free software; you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation; either version 2, or (at your option)\n\
any later version.\n\
\n\
This program is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
GNU General Public License for more details.\n\
\n\
You should have received a copy of the GNU General Public License\n\
along with this program; if not, write to the Free Software\n\
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.\n");
}
/*
* Display usage to the user.
*/
2001-11-22 08:31:10 +08:00
static void
display_usage(void)
{
printf("Usage: %s [options]\n", PACKAGE);
printf("\
Options:\n\
-d Operate in DEBUG mode.\n\
-c FILE Use an alternate configuration file.\n\
-h Display this usage information.\n\
-l Display the license.\n\
-v Display the version number.\n");
/* Display the modes compiled into tinyproxy */
printf("\nFeatures Compiled In:\n");
#ifdef XTINYPROXY_ENABLE
printf(" XTinyproxy Header\n");
#endif /* XTINYPROXY */
#ifdef FILTER_ENABLE
printf(" Filtering\n");
printf(" * with Regular Expression support\n");
#endif /* FILTER_ENABLE */
#ifndef NDEBUG
2000-11-23 12:46:48 +08:00
printf(" Debugging code\n");
#endif /* NDEBUG */
#ifdef TUNNEL_SUPPORT
printf(" TCP Tunneling\n");
#endif /* TUNNEL_SUPPORT */
}
2001-11-22 08:31:10 +08:00
int
main(int argc, char **argv)
{
int optch;
bool_t godaemon = TRUE;
struct passwd *thisuser = NULL;
struct group *thisgroup = NULL;
char *conf_file = DEFAULT_CONF_FILE;
/*
* Disable the creation of CORE files right up front.
*/
#ifdef HAVE_SETRLIMIT
2001-11-22 08:31:10 +08:00
struct rlimit core_limit = { 0, 0 };
if (setrlimit(RLIMIT_CORE, &core_limit) < 0) {
2001-11-22 08:31:10 +08:00
fprintf(stderr, "%s: Could not set the core limit to zero.\n",
argv[0]);
exit(EX_SOFTWARE);
}
2001-11-22 08:31:10 +08:00
#endif /* HAVE_SETRLIMIT */
/*
* Process the various options
*/
2001-11-22 08:31:10 +08:00
while ((optch = getopt(argc, argv, "c:vldh")) != EOF) {
switch (optch) {
case 'v':
display_version();
exit(EX_OK);
case 'l':
display_license();
exit(EX_OK);
case 'd':
godaemon = FALSE;
break;
case 'c':
conf_file = safestrdup(optarg);
if (!conf_file) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: Could not allocate memory.\n",
argv[0]);
exit(EX_SOFTWARE);
}
break;
case 'h':
default:
2001-11-22 08:31:10 +08:00
display_usage();
exit(EX_OK);
}
}
/*
* Read in the settings from the config file.
*/
yyin = fopen(conf_file, "r");
if (!yyin) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: Could not open configuration file \"%s\".\n",
argv[0], conf_file);
exit(EX_SOFTWARE);
}
yyparse();
#if defined(TUNNEL_SUPPORT) && defined(UPSTREAM_SUPPORT)
if (config.tunnel_name && config.upstream_name) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: \"Tunnel\" and \"Upstream\" directives can not be both set.\n",
argv[0]);
exit(EX_SOFTWARE);
}
#endif
/* Open the log file if not using syslog */
if (config.syslog == FALSE) {
int log_file_fd;
if (!config.logf_name) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: You MUST set a LogFile in the configuration file.\n",
argv[0]);
exit(EX_SOFTWARE);
}
log_file_fd = create_file_safely(config.logf_name);
if (log_file_fd < 0) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"Could not safely create logfile \"%s\".\n",
config.logf_name);
exit(EX_CANTCREAT);
}
config.logf = fdopen(log_file_fd, "w");
if (!config.logf) {
2001-11-22 08:31:10 +08:00
fprintf(stderr, "Could not write to log file \"%s\".\n",
config.logf_name);
exit(EX_CANTCREAT);
}
} else {
if (godaemon == TRUE)
openlog("tinyproxy", LOG_PID, LOG_DAEMON);
else
openlog("tinyproxy", LOG_PID, LOG_USER);
}
log_message(LOG_INFO, PACKAGE " " VERSION " starting...");
/*
* Set the default values if they were not set in the config file.
*/
if (config.port == 0) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: You MUST set a Port in the configuration file.\n",
argv[0]);
exit(EX_SOFTWARE);
}
if (!config.stathost) {
2001-11-22 08:31:10 +08:00
log_message(LOG_INFO, "Setting stathost to \"%s\".",
DEFAULT_STATHOST);
config.stathost = DEFAULT_STATHOST;
}
if (!config.username) {
2001-11-22 08:31:10 +08:00
log_message(LOG_WARNING,
"You SHOULD set a UserName in the configuration file. Using current user instead.");
}
if (config.idletimeout == 0) {
log_message(LOG_WARNING, "Invalid idle time setting. Only values greater than zero allowed; therefore setting idle timeout to %u seconds.",
2001-11-22 08:31:10 +08:00
MAX_IDLE_TIME);
config.idletimeout = MAX_IDLE_TIME;
}
init_stats();
/*
* If ANONYMOUS is turned on, make sure that Content-Length is
* in the list of allowed headers, since it is required in a
* HTTP/1.0 request. Also add the Content-Type header since it goes
* hand in hand with Content-Length.
* - rjkaes
*/
if (is_anonymous_enabled()) {
anonymous_insert("Content-Length");
anonymous_insert("Content-Type");
}
if (godaemon == TRUE)
makedaemon();
if (config.pidpath) {
pidfile_create(config.pidpath);
}
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
2001-11-22 08:31:10 +08:00
fprintf(stderr, "%s: Could not set the \"SIGPIPE\" signal.\n",
argv[0]);
exit(EX_OSERR);
}
#ifdef FILTER_ENABLE
if (config.filter)
filter_init();
#endif /* FILTER_ENABLE */
/*
* Start listening on the selected port.
*/
if (thread_listening_sock(config.port) < 0) {
2001-11-22 08:31:10 +08:00
fprintf(stderr, "%s: Could not create listening socket.\n",
argv[0]);
exit(EX_OSERR);
}
/*
* Switch to a different user.
*/
if (geteuid() == 0) {
if (config.group && strlen(config.group) > 0) {
thisgroup = getgrnam(config.group);
if (!thisgroup) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: Unable to find group \"%s\".\n",
argv[0], config.group);
exit(EX_NOUSER);
}
if (setgid(thisgroup->gr_gid) < 0) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: Unable to change to group \"%s\".\n",
argv[0], config.group);
exit(EX_CANTCREAT);
}
2001-11-22 08:31:10 +08:00
log_message(LOG_INFO, "Now running as group \"%s\".",
config.group);
}
if (config.username && strlen(config.username) > 0) {
thisuser = getpwnam(config.username);
if (!thisuser) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: Unable to find user \"%s\".",
argv[0], config.username);
exit(EX_NOUSER);
}
if (setuid(thisuser->pw_uid) < 0) {
2001-11-22 08:31:10 +08:00
fprintf(stderr,
"%s: Unable to change to user \"%s\".",
argv[0], config.username);
exit(EX_CANTCREAT);
}
2001-11-22 08:31:10 +08:00
log_message(LOG_INFO, "Now running as user \"%s\".",
config.username);
}
} else {
2001-11-22 08:31:10 +08:00
log_message(LOG_WARNING,
"Not running as root, so not changing UID/GID.");
}
if (thread_pool_create() < 0) {
2001-11-22 08:31:10 +08:00
fprintf(stderr, "%s: Could not create the pool of threads.",
argv[0]);
exit(EX_SOFTWARE);
}
/*
* These signals are only for the main thread.
*/
log_message(LOG_INFO, "Setting the various signals.");
if (signal(SIGTERM, takesig) == SIG_ERR) {
2001-11-22 08:31:10 +08:00
fprintf(stderr, "%s: Could not set the \"SIGTERM\" signal.\n",
argv[0]);
exit(EX_OSERR);
}
if (signal(SIGHUP, takesig) == SIG_ERR) {
2001-11-22 08:31:10 +08:00
fprintf(stderr, "%s: Could not set the \"SIGHUP\" signal.\n",
argv[0]);
exit(EX_OSERR);
}
/*
* Start the main loop.
*/
log_message(LOG_INFO, "Starting main loop. Accepting connections.");
thread_main_loop();
#ifdef FILTER_ENABLE
if (config.filter)
filter_destroy();
#endif
log_message(LOG_INFO, "Shutting down.");
thread_kill_threads();
thread_close_sock();
/*
* Remove the PID file.
*/
if (unlink(config.pidpath) < 0) {
2001-11-22 08:31:10 +08:00
log_message(LOG_WARNING,
"Could not remove PID file \"%s\": %s.",
config.pidpath, strerror(errno));
}
#ifdef FILTER_ENABLE
if (config.filter)
filter_destroy();
#endif /* FILTER_ENABLE */
if (config.syslog == FALSE)
fclose(config.logf);
else
closelog();
exit(EX_OK);
}