blob: 06377dc30dcd36700476ab7ef47e1094fc89b738 [file] [log] [blame]
/** @file
A brief file description
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/****************************************************************************
Main.cc
This is the primary source file for the proxy cache system.
****************************************************************************/
#include "ink_config.h"
#include "libts.h"
#if !defined(linux)
#include <sys/lock.h>
#endif
#include <sys/resource.h>
#if defined(linux)
extern "C" int plock(int);
#else
#include <sys/filio.h>
#endif
#include <syslog.h>
#if !defined(darwin) && !defined(freebsd) && !defined(solaris)
#include <mcheck.h>
#endif
#if TS_USE_POSIX_CAP
#include <sys/capability.h>
#endif
#include "Main.h"
#include "signals.h"
#include "Error.h"
#include "StatSystem.h"
#include "P_EventSystem.h"
#include "P_Net.h"
#include "P_DNS.h"
#include "P_SplitDNS.h"
#include "P_Cluster.h"
#include "P_HostDB.h"
#include "P_Cache.h"
#include "I_Layout.h"
#include "I_Machine.h"
#include "RecordsConfig.h"
#include "Transform.h"
#include "ProcessManager.h"
#include "ProxyConfig.h"
#include "HttpProxyServerMain.h"
#include "HttpBodyFactory.h"
#include "logging/Log.h"
#include "ICPProcessor.h"
//#include "ClusterTest.h"
#include "CacheControl.h"
#include "IPAllow.h"
#include "ParentSelection.h"
//#include "simple/Simple.h"
#include "MgmtUtils.h"
#include "StatPages.h"
#include "HTTP.h"
#include "Plugin.h"
#include "DiagsConfig.h"
#include "CoreUtils.h"
#include "Update.h"
#include "congest/Congestion.h"
#include "RemapProcessor.h"
#include "XmlUtils.h"
#include "I_Tasks.h"
#if TS_HAS_V2STATS
#include "StatSystemV2.h"
#endif
#if TS_HAS_PROFILER
#include <google/profiler.h>
#endif
//
// Global Data
//
#define DEFAULT_NUMBER_OF_THREADS ink_number_of_processors()
#define DEFAULT_NUMBER_OF_UDP_THREADS 1
#define DEFAULT_NUMBER_OF_CLUSTER_THREADS 1
#define DEFAULT_NUMBER_OF_SSL_THREADS 0
#define DEFAULT_NUM_ACCEPT_THREADS 0
#define DEFAULT_NUM_TASK_THREADS 0
#define DEFAULT_HTTP_ACCEPT_PORT_NUMBER 0
#define DEFAULT_COMMAND_FLAG 0
#define DEFAULT_LOCK_PROCESS 0
#define DEFAULT_VERBOSE_FLAG 0
#define DEFAULT_VERSION_FLAG 0
#define DEFAULT_STACK_TRACE_FLAG 0
#if DEFAULT_COMMAND_FLAG
# define DEFAULT_COMMAND_FLAG_TYPE "f"
#else
# define DEFAULT_COMMAND_FLAG_TYPE "F"
#endif
#define DEFAULT_REMOTE_MANAGEMENT_FLAG 0
int version_flag = DEFAULT_VERSION_FLAG;
int stack_trace_flag = DEFAULT_STACK_TRACE_FLAG;
int number_of_processors = ink_number_of_processors();
int num_of_net_threads = DEFAULT_NUMBER_OF_THREADS;
int num_of_cluster_threads = DEFAULT_NUMBER_OF_CLUSTER_THREADS;
int num_of_udp_threads = DEFAULT_NUMBER_OF_UDP_THREADS;
int num_of_ssl_threads = DEFAULT_NUMBER_OF_SSL_THREADS;
int num_accept_threads = DEFAULT_NUM_ACCEPT_THREADS;
int num_task_threads = DEFAULT_NUM_TASK_THREADS;
int run_test_hook = 0;
int http_accept_port_number = DEFAULT_HTTP_ACCEPT_PORT_NUMBER;
int http_accept_file_descriptor = NO_FD;
int ssl_accept_file_descriptor = NO_FD;
char accept_fd_list[1024] = "";
char core_file[255] = "";
int command_flag = DEFAULT_COMMAND_FLAG;
#if TS_HAS_TESTS
char regression_test[1024] = "";
#endif
int auto_clear_hostdb_flag = 0;
int lock_process = DEFAULT_LOCK_PROCESS;
extern int fds_limit;
extern int cluster_port_number;
extern int cache_clustering_enabled;
char cluster_host[DOMAIN_NAME_MAX + 1] = DEFAULT_CLUSTER_HOST;
// = DEFAULT_CLUSTER_PORT_NUMBER;
char proxy_name[DOMAIN_NAME_MAX + 1] = "unknown";
char command_string[512] = "";
int remote_management_flag = DEFAULT_REMOTE_MANAGEMENT_FLAG;
char management_directory[PATH_NAME_MAX+1]; // Layout->sysconfdir
char system_root_dir[PATH_NAME_MAX + 1]; // Layout->prefix
char system_runtime_dir[PATH_NAME_MAX + 1]; // Layout->runtimedir
char system_config_directory[PATH_NAME_MAX + 1]; // Layout->sysconfdir
char system_log_dir[PATH_NAME_MAX + 1]; // Layout->logdir
int logging_port_override = 0;
char logging_server_override[256] = " do not override";
char error_tags[1024] = "";
char action_tags[1024] = "";
int show_statistics = 0;
int history_info_enabled = 1;
inkcoreapi Diags *diags = NULL;
inkcoreapi DiagsConfig *diagsConfig = NULL;
HttpBodyFactory *body_factory = NULL;
int diags_init = 0; // used by process manager
char vingid_flag[255] = "";
static int accept_mss = 0;
static int cmd_line_dprintf_level = 0; // default debug output level fro ink_dprintf function
AppVersionInfo appVersionInfo; // Build info for this application
#if TS_HAS_TESTS
extern int run_TestHook();
// TODO: Maybe review and "fix" this test at some point?
//
//extern void run_SimpleHttp();
#endif
void deinitSubAgent();
Version version = {
{CACHE_DB_MAJOR_VERSION, CACHE_DB_MINOR_VERSION}, // cacheDB
{CACHE_DIR_MAJOR_VERSION, CACHE_DIR_MINOR_VERSION}, // cacheDir
{CLUSTER_MAJOR_VERSION, CLUSTER_MINOR_VERSION}, // current clustering
{MIN_CLUSTER_MAJOR_VERSION, MIN_CLUSTER_MINOR_VERSION}, // min clustering
};
ArgumentDescription argument_descriptions[] = {
{"lock_memory", 'l', "Lock process in memory (must be root)",
"I", &lock_process, "PROXY_LOCK_PROCESS", NULL},
{"net_threads", 'n', "Number of Net Threads", "I", &num_of_net_threads,
"PROXY_NET_THREADS", NULL},
{"cluster_threads", 'Z', "Number of Cluster Threads", "I",
&num_of_cluster_threads, "PROXY_CLUSTER_THREADS", NULL},
{"udp_threads", 'U', "Number of UDP Threads", "I",
&num_of_udp_threads, "PROXY_UDP_THREADS", NULL},
{"accept_thread", 'a', "Use an Accept Thread", "T", &num_accept_threads,
"PROXY_ACCEPT_THREAD", NULL},
{"accept_till_done", 'b', "Accept Till Done", "T", &accept_till_done,
"PROXY_ACCEPT_TILL_DONE", NULL},
{"httpport", 'p', "Port Number for HTTP Accept", "I",
&http_accept_port_number, "PROXY_HTTP_ACCEPT_PORT", NULL},
{"acceptfds", 'A', "File Descriptor List for Accept", "S1023",
accept_fd_list, "PROXY_ACCEPT_DESCRIPTOR_LIST", NULL},
{"cluster_port", 'P', "Cluster Port Number", "I", &cluster_port_number,
"PROXY_CLUSTER_PORT", NULL},
{"dprintf_level", 'o', "Debug output level", "I", &cmd_line_dprintf_level,
"PROXY_DPRINTF_LEVEL", NULL},
{"version", 'V', "Print Version String", "T", &version_flag,
NULL, NULL},
#if TS_HAS_TESTS
{"regression", 'R',
#ifdef DEBUG
"Regression Level (quick:1..long:3)",
#else
0,
#endif
"I", &regression_level, "PROXY_REGRESSION", NULL},
{"regression_test", 'r',
#ifdef DEBUG
"Run Specific Regression Test",
#else
0,
#endif
"S512", regression_test, "PROXY_REGRESSION_TEST", NULL},
{"test_hook", 'H',
#ifdef DEBUG
"Run Test Stub Instead of Server",
#else
0,
#endif
"T",
&run_test_hook, "PROXY_RUN_TEST_HOOK", NULL},
#endif //TS_HAS_TESTS
#if TS_USE_DIAGS
{"debug_tags", 'T', "Vertical-bar-separated Debug Tags", "S1023", error_tags,
"PROXY_DEBUG_TAGS", NULL},
{"action_tags", 'B', "Vertical-bar-separated Behavior Tags", "S1023", action_tags,
"PROXY_BEHAVIOR_TAGS", NULL},
#endif
{"interval", 'i', "Statistics Interval", "I", &show_statistics,
"PROXY_STATS_INTERVAL", NULL},
{"remote_management", 'M', "Remote Management", "T",
&remote_management_flag, "PROXY_REMOTE_MANAGEMENT", NULL},
{"management_dir", 'd', "Management Directory", "S255",
&management_directory, "PROXY_MANAGEMENT_DIRECTORY", NULL},
{"command", 'C', "Maintenance Command to Execute", "S511",
&command_string, "PROXY_COMMAND_STRING", NULL},
// {"clear_authdb", 'j', "Clear AuthDB on Startup", "F",
// &auto_clear_authdb_flag, "PROXY_CLEAR_AUTHDB", NULL},
{"clear_hostdb", 'k', "Clear HostDB on Startup", "F",
&auto_clear_hostdb_flag, "PROXY_CLEAR_HOSTDB", NULL},
{"clear_cache", 'K', "Clear Cache on Startup", "F",
&cacheProcessor.auto_clear_flag, "PROXY_CLEAR_CACHE", NULL},
{"vingid", 'v', "Vingid Flag", "S255", vingid_flag, "PROXY_VINGID", NULL},
#if defined(linux)
{"read_core", 'c', "Read Core file", "S255",
&core_file, NULL, NULL},
#endif
{"accept_mss", ' ', "MSS for client connections", "I", &accept_mss,
NULL, NULL},
{"poll_timeout", 't', "poll timeout in milliseconds", "I", &net_config_poll_timeout,
NULL, NULL},
{"help", 'h', "HELP!", NULL, NULL, NULL, usage},
};
int n_argument_descriptions = SIZE(argument_descriptions);
#define set_rlimit(name,max_it,ulim_it) max_out_limit(#name, name, max_it, ulim_it)
static rlim_t
max_out_limit(const char *name, int which, bool max_it = true, bool unlim_it = true)
{
NOWARN_UNUSED(name);
struct rlimit rl;
#if defined(linux)
# define MAGIC_CAST(x) (enum __rlimit_resource)(x)
#else
# define MAGIC_CAST(x) x
#endif
if (max_it) {
ink_release_assert(getrlimit(MAGIC_CAST(which), &rl) >= 0);
if (rl.rlim_cur != rl.rlim_max) {
#if defined(darwin)
if (which == RLIMIT_NOFILE)
rl.rlim_cur = fmin(OPEN_MAX, rl.rlim_max);
else
rl.rlim_cur = rl.rlim_max;
#else
rl.rlim_cur = rl.rlim_max;
#endif
ink_release_assert(setrlimit(MAGIC_CAST(which), &rl) >= 0);
}
}
if (unlim_it) {
ink_release_assert(getrlimit(MAGIC_CAST(which), &rl) >= 0);
if (rl.rlim_cur != (rlim_t)RLIM_INFINITY) {
rl.rlim_cur = (rl.rlim_max = RLIM_INFINITY);
ink_release_assert(setrlimit(MAGIC_CAST(which), &rl) >= 0);
}
}
ink_release_assert(getrlimit(MAGIC_CAST(which), &rl) >= 0);
//syslog(LOG_NOTICE, "NOTE: %s(%d):cur(%d),max(%d)", name, which, (int)rl.rlim_cur, (int)rl.rlim_max);
return rl.rlim_cur;
}
//
// Initialize operating system related information/services
//
void
init_system()
{
RecInt stackDump;
bool found = (RecGetRecordInt("proxy.config.stack_dump_enabled", &stackDump) == REC_ERR_OKAY);
if(found == false) {
Warning("Unable to determine stack_dump_enabled , assuming enabled");
stackDump = 1;
}
init_signals(stackDump == 1);
syslog(LOG_NOTICE, "NOTE: --- Server Starting ---");
syslog(LOG_NOTICE, "NOTE: Server Version: %s", appVersionInfo.FullVersionInfoStr);
//
// Check cycle counter resolution
//
//
// Delimit file Descriptors
//
fds_limit = set_rlimit(RLIMIT_NOFILE, true, false);
}
static void
check_lockfile()
{
char *lockfile = NULL;
pid_t holding_pid;
int err;
#ifndef _DLL_FOR_HNS
if (access(Layout::get()->runtimedir, R_OK | W_OK) == -1) {
fprintf(stderr,"unable to access() dir'%s': %d, %s\n",
Layout::get()->runtimedir, errno, strerror(errno));
fprintf(stderr," please set correct path in env variable TS_ROOT \n");
_exit(1);
}
lockfile = Layout::relative_to(Layout::get()->runtimedir, SERVER_LOCK);
#else
#define MAX_ENVVAR_LENGTH 128
char tempvar[MAX_ENVVAR_LENGTH + 1];
// TODO: Need an portable ink_file_tmppath()
// XXX: What's the _DLL_FOR_HS?
//
ink_assert(GetEnvironmentVariable("TEMP", tempvar, MAX_ENVVAR_LENGTH + 1));
lockfile = Layout::relative_to(tempvar, SERVER_LOCK);
#endif
Lockfile server_lockfile(lockfile);
err = server_lockfile.Get(&holding_pid);
if (err != 1) {
char *reason = strerror(-err);
fprintf(stderr, "WARNING: Can't acquire lockfile '%s'", lockfile);
if ((err == 0) && (holding_pid != -1)) {
#if defined(solaris)
fprintf(stderr, " (Lock file held by process ID %d)\n", (int)holding_pid);
#else
fprintf(stderr, " (Lock file held by process ID %d)\n", holding_pid);
#endif
} else if ((err == 0) && (holding_pid == -1)) {
fprintf(stderr, " (Lock file exists, but can't read process ID)\n");
} else if (reason) {
fprintf(stderr, " (%s)\n", reason);
} else {
fprintf(stderr, "\n");
}
_exit(1);
}
xfree(lockfile);
}
static void
init_dirs(void)
{
char buf[PATH_NAME_MAX + 1];
ink_strlcpy(system_config_directory, Layout::get()->sysconfdir, PATH_NAME_MAX);
ink_strlcpy(system_runtime_dir, Layout::get()->runtimedir, PATH_NAME_MAX);
ink_strlcpy(system_log_dir, Layout::get()->logdir, PATH_NAME_MAX);
/*
* XXX: There is not much sense in the following code
* The purpose of proxy.config.foo_dir should
* be checked BEFORE checking default foo directory.
* Otherwise one cannot change the config dir to something else
*/
if (access(system_config_directory, R_OK) == -1) {
REC_ReadConfigString(buf, "proxy.config.config_dir", PATH_NAME_MAX);
Layout::get()->relative(system_config_directory, PATH_NAME_MAX, buf);
if (access(system_config_directory, R_OK) == -1) {
fprintf(stderr,"unable to access() config dir '%s': %d, %s\n",
system_config_directory, errno, strerror(errno));
fprintf(stderr, "please set config path via 'proxy.config.config_dir' \n");
_exit(1);
}
}
if (access(system_runtime_dir, R_OK | W_OK) == -1) {
REC_ReadConfigString(buf, "proxy.config.local_state_dir", PATH_NAME_MAX);
Layout::get()->relative(system_runtime_dir, PATH_NAME_MAX, buf);
if (access(system_runtime_dir, R_OK | W_OK) == -1) {
fprintf(stderr,"unable to access() local state dir '%s': %d, %s\n",
system_runtime_dir, errno, strerror(errno));
fprintf(stderr,"please set 'proxy.config.local_state_dir'\n");
_exit(1);
}
}
if (access(system_log_dir, W_OK) == -1) {
REC_ReadConfigString(buf, "proxy.config.log.logfile_dir", PATH_NAME_MAX);
Layout::get()->relative(system_log_dir, PATH_NAME_MAX, buf);
if (access(system_log_dir, W_OK) == -1) {
fprintf(stderr,"unable to access() log dir'%s':%d, %s\n",
system_log_dir, errno, strerror(errno));
fprintf(stderr,"please set 'proxy.config.log.logfile_dir'\n");
_exit(1);
}
}
}
//
// Startup process manager
//
static void
initialize_process_manager()
{
mgmt_use_syslog();
// Temporary Hack to Enable Communuication with LocalManager
if (getenv("PROXY_REMOTE_MGMT")) {
remote_management_flag = true;
}
if (access(management_directory, R_OK) == -1) {
ink_strncpy(management_directory, Layout::get()->sysconfdir, PATH_NAME_MAX);
if (access(management_directory, R_OK) == -1) {
fprintf(stderr,"unable to access() management path '%s': %d, %s\n", management_directory, errno, strerror(errno));
fprintf(stderr,"please set management path via command line '-d <managment directory>'\n");
_exit(1);
}
}
RecProcessInit(remote_management_flag ? RECM_CLIENT : RECM_STAND_ALONE, diags);
if (!remote_management_flag) {
LibRecordsConfigInit();
}
//
// Start up manager
//
pmgmt = NEW(new ProcessManager(remote_management_flag, management_directory));
pmgmt->start();
RecProcessInitMessage(remote_management_flag ? RECM_CLIENT : RECM_STAND_ALONE);
pmgmt->reconfigure();
init_dirs();// setup directories
//
// Define version info records
//
RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.short", appVersionInfo.VersionStr, RECP_NULL);
RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.long", appVersionInfo.FullVersionInfoStr, RECP_NULL);
RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_number", appVersionInfo.BldNumStr, RECP_NULL);
RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_time", appVersionInfo.BldTimeStr, RECP_NULL);
RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_date", appVersionInfo.BldDateStr, RECP_NULL);
RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_machine", appVersionInfo.BldMachineStr, RECP_NULL);
RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_person", appVersionInfo.BldPersonStr, RECP_NULL);
}
//
// Shutdown
//
void
shutdown_system()
{
}
#define CMD_ERROR -2 // serious error, exit maintaince mode
#define CMD_FAILED -1 // error, but recoverable
#define CMD_OK 0 // ok, or minor (user) error
#define CMD_HELP 1 // ok, print help
#define CMD_IN_PROGRESS 2 // task not completed. don't exit
static int
cmd_list(char *cmd)
{
(void) cmd;
printf("LIST\n\n");
// show hostdb size
#ifndef INK_NO_HOSTDB
int h_size = 0;
TS_ReadConfigInteger(h_size, "proxy.config.hostdb.size");
printf("Host Database size:\t%d\n", h_size);
#endif
// show cache config information....
Note("Cache Storage:");
Store tStore;
if (tStore.read_config() < 0) {
Note("config read failure");
return CMD_FAILED;
} else {
tStore.write_config_data(fileno(stdout));
return CMD_OK;
}
}
static char *
skip(char *cmd, int null_ok = 0)
{
cmd += strspn(cmd, " \t");
cmd = strpbrk(cmd, " \t");
if (!cmd) {
if (!null_ok)
printf("Error: argument missing\n");
return cmd;
}
cmd += strspn(cmd, " \t");
return cmd;
}
struct CmdCacheCont: public Continuation
{
int cache_fix;
int ClearEvent(int event, Event * e)
{
(void) event;
(void) e;
if (cacheProcessor.IsCacheEnabled() == CACHE_INITIALIZED) {
Note("CLEAR, succeeded");
_exit(0);
} else if (cacheProcessor.IsCacheEnabled() == CACHE_INIT_FAILED) {
Note("unable to open Cache, CLEAR failed");
_exit(1);
}
return EVENT_CONT;
}
int CheckEvent(int event, Event * e)
{
(void) event;
(void) e;
int res = 0;
Note("Cache Directory");
if (cacheProcessor.IsCacheEnabled() == CACHE_INITIALIZED) {
res = cacheProcessor.dir_check(cache_fix) < 0 || res;
Note("Cache");
res = cacheProcessor.db_check(cache_fix) < 0 || res;
cacheProcessor.stop();
const char *n = cache_fix ? "REPAIR" : "CHECK";
if (res) {
printf("\n%s failed", n);
_exit(1);
} else {
printf("\n%s succeeded\n", n);
_exit(0);
}
} else if (cacheProcessor.IsCacheEnabled() == CACHE_INIT_FAILED) {
Note("unable to open Cache, Check failed");
_exit(1);
}
return EVENT_CONT;
}
CmdCacheCont(bool check, bool fix = false):Continuation(new_ProxyMutex()) {
cache_fix = fix;
if (check)
SET_HANDLER(&CmdCacheCont::CheckEvent);
else
SET_HANDLER(&CmdCacheCont::ClearEvent);
}
};
static int
cmd_check_internal(char *cmd, bool fix = false)
{
NOWARN_UNUSED(cmd);
const char *n = fix ? "REPAIR" : "CHECK";
printf("%s\n\n", n);
int res = 0;
#ifndef INK_NO_HOSTDB
hostdb_current_interval = (ink_get_based_hrtime() / HRTIME_MINUTE);
#endif
//#ifndef INK_NO_ACC
// acc.clear_cache();
//#endif
const char *err = NULL;
theStore.delete_all();
if ((err = theStore.read_config())) {
printf("%s, %s failed\n", err, n);
return CMD_FAILED;
}
#ifndef INK_NO_HOSTDB
printf("Host Database\n");
HostDBCache hd;
if (hd.start(fix) < 0) {
printf("\tunable to open Host Database, %s failed\n", n);
return CMD_OK;
}
res = hd.check("hostdb.config", fix) < 0 || res;
hd.reset();
#endif
if (cacheProcessor.start() < 0) {
printf("\nbad cache configuration, %s failed\n", n);
return CMD_FAILED;
}
eventProcessor.schedule_every(NEW(new CmdCacheCont(true, fix)), HRTIME_SECONDS(1));
return CMD_IN_PROGRESS;
}
static int
cmd_check(char *cmd)
{
return cmd_check_internal(cmd, false);
}
#ifdef UNUSED_FUNCTION
static int
cmd_repair(char *cmd)
{
return cmd_check_internal(cmd, true);
}
#endif
static int
cmd_clear(char *cmd)
{
Note("CLEAR");
bool c_all = !strcmp(cmd, "clear");
bool c_hdb = !strcmp(cmd, "clear_hostdb");
//bool c_adb = !strcmp(cmd, "clear_authdb");
bool c_cache = !strcmp(cmd, "clear_cache");
char p[PATH_NAME_MAX];
if (c_all || c_hdb) {
Note("Clearing Configuration");
Layout::relative_to(p, sizeof(p), system_config_directory,
"internal/hostdb.config");
if (unlink(p) < 0)
Note("unable to unlink %s", p);
}
if (c_all || c_cache) {
const char *err = NULL;
theStore.delete_all();
if ((err = theStore.read_config())) {
printf("%s, CLEAR failed\n", err);
return CMD_FAILED;
}
}
#ifndef INK_NO_HOSTDB
if (c_hdb || c_all) {
Note("Clearing Host Database");
if (hostDBProcessor.cache()->start(PROCESSOR_RECONFIGURE) < 0) {
Note("unable to open Host Database, CLEAR failed");
return CMD_FAILED;
}
hostDBProcessor.cache()->reset();
if (c_hdb)
return CMD_OK;
}
#endif
//#ifndef INK_NO_ACC
// if (c_adb || c_all) {
// if (!acc.clear_cache()) {
// return CMD_FAILED;
// }
// if (c_adb)
// return CMD_OK;
// }
//#endif
if (c_all || c_cache) {
Note("Clearing Cache");
if (cacheProcessor.start_internal(PROCESSOR_RECONFIGURE) < 0) {
Note("unable to open Cache, CLEAR failed");
return CMD_FAILED;
}
eventProcessor.schedule_every(NEW(new CmdCacheCont(false)), HRTIME_SECONDS(1));
return CMD_IN_PROGRESS;
}
return CMD_OK;
}
static int cmd_help(char *cmd);
static struct CMD
{
const char *n; // name
const char *d; // description (part of a line)
const char *h; // help string (multi-line)
int (*f) (char *);
}
commands[] = {
{
"list",
"List cache configuration",
"LIST\n"
"\n"
"FORMAT: list\n"
"\n"
"List the sizes of the Host Database and Cache Index,\n" "and the storage available to the cache.\n", cmd_list}, {
"check",
"Check the cache (do not make any changes)",
"CHECK\n"
"\n"
"FORMAT: check\n"
"\n"
"Check the cache for inconsistencies or corruption.\n"
"CHECK does not make any changes to the data stored in\n"
"the cache. CHECK requires a scan of the contents of the\n"
"cache and may take a long time for large caches.\n", cmd_check}, {
"clear",
"Clear the entire cache",
"CLEAR\n"
"\n"
"FORMAT: clear\n"
"\n"
"Clear the entire cache. All data in the cache is\n"
"lost and the cache is reconfigured based on the current\n"
"description of database sizes and available storage.\n", cmd_clear}, {
"clear_cache",
"Clear the document cache",
"CLEAR_CACHE\n"
"\n"
"FORMAT: clear_cache\n"
"\n"
"Clear the document cache. All documents in the cache are\n"
"lost and the cache is reconfigured based on the current\n"
"description of database sizes and available storage.\n", cmd_clear}, {
"clear_hostdb",
"Clear the hostdb cache",
"CLEAR_HOSTDB\n"
"\n"
"FORMAT: clear_hostdb\n"
"\n" "Clear the entire hostdb cache. All host name resolution\n" "information is lost.\n", cmd_clear}, {
"help",
"Obtain a short description of a command (e.g. 'help clear')",
"HELP\n"
"\n"
"FORMAT: help [command_name]\n"
"\n"
"EXAMPLES: help help\n"
" help commit\n" "\n" "Provide a short description of a command (like this).\n", cmd_help},};
#define N_CMDS ((int)(sizeof(commands)/sizeof(commands[0])))
static int
cmd_index(char *p)
{
p += strspn(p, " \t");
for (int c = 0; c < N_CMDS; c++) {
const char *l = commands[c].n;
while (l) {
const char *s = strchr(l, '/');
char *e = strpbrk(p, " \t\n");
int len = s ? s - l : strlen(l);
int lenp = e ? e - p : strlen(p);
if ((len == lenp) && !strncasecmp(p, l, len))
return c;
l = s ? s + 1 : 0;
}
}
return -1;
}
static int
cmd_help(char *cmd)
{
int i;
(void) cmd;
printf("HELP\n\n");
cmd = skip(cmd, true);
if (!cmd) {
for (i = 0; i < N_CMDS; i++) {
printf("%15s %s\n", commands[i].n, commands[i].d);
}
} else {
if ((i = cmd_index(cmd)) < 0) {
printf("\nno help found for: %s\n", cmd);
return CMD_FAILED;
}
printf("Help for: %s\n\n", commands[i].n);
printf("%s", commands[i].h);
}
return CMD_OK;
}
static void
check_fd_limit()
{
int fds_throttle = -1;
TS_ReadConfigInteger(fds_throttle, "proxy.config.net.connections_throttle");
if (fds_throttle > fds_limit + THROTTLE_FD_HEADROOM) {
int new_fds_throttle = fds_limit - THROTTLE_FD_HEADROOM;
if (new_fds_throttle < 1)
MachineFatal("too few file descritors (%d) available", fds_limit);
char msg[256];
snprintf(msg, sizeof(msg), "connection throttle too high, "
"%d (throttle) + %d (internal use) > %d (file descriptor limit), "
"using throttle of %d", fds_throttle, THROTTLE_FD_HEADROOM, fds_limit, new_fds_throttle);
SignalWarning(MGMT_SIGNAL_SYSTEM_ERROR, msg);
}
}
//
// Command mode
//
static int
cmd_mode()
{
if (*command_string) {
int c = cmd_index(command_string);
if (c >= 0) {
return commands[c].f(command_string);
} else {
Warning("unrecognized command: '%s'", command_string);
return CMD_FAILED; // in error
}
} else {
printf("\n");
printf("WARNING\n");
printf("\n");
printf("The interactive command mode no longer exists.\n");
printf("Use '-C <command>' to execute a command from the shell prompt.\n");
printf("For example: 'traffic_server -C clear' will clear the cache.\n");
return 1;
}
}
#ifdef UNUSED_FUNCTION
static void
check_for_root_uid()
{
if ((getuid() == 0) || (geteuid() == 0)) {
ProcessFatal("Traffic Server must not be run as root");
}
}
#endif
// static void print_accept_fd(HttpPortEntry* e)
//
static void
print_accept_fd(HttpPortEntry * e)
{
if (e) {
printf("Accept FDs: ");
while (e->fd != NO_FD) {
printf("%d:%d ", e->fd, e->type);
e++;
}
printf("\n");
}
}
// static HttpPortEntry* parse_accept_fd_list()
//
// Parses the list of FD's and types sent in by the manager
// with the -A flag
//
// If the NTTP Accept fd is in the list, sets global
// nttp_accept_fd
//
// If the SSL Accept fd is in the list, sets global
// ssl_accept_fd
//
// If there is no -A arg, returns NULL
//
// Otherwise returns an array of HttpPortEntry which
// is terminated with a HttpPortEntry with the fd
// field set to NO_FD
//
static HttpPortEntry *
parse_accept_fd_list()
{
HttpPortEntry *accept_array;
int accept_index = 0;
int list_entries;
char *cur_entry;
char *attr_str;
HttpPortTypes attr = SERVER_PORT_DEFAULT;;
int fd = 0;
Tokenizer listTok(",");
if (!accept_fd_list[0] || (list_entries = listTok.Initialize(accept_fd_list, SHARE_TOKS)) <= 0)
return 0;
accept_array = new HttpPortEntry[list_entries + 1];
accept_array[0].fd = NO_FD;
for (int i = 0; i < list_entries; i++) {
cur_entry = (char *) listTok[i];
// Check to see if there is a port attribute
attr_str = strchr(cur_entry, ':');
if (attr_str != NULL) {
*attr_str = '\0';
attr_str = attr_str + 1;
}
// Handle the file descriptor
fd = strtoul(cur_entry, NULL, 10);
// Handle reading the attribute
if (attr_str == NULL) {
attr = SERVER_PORT_DEFAULT;
} else {
if (strlen(attr_str) > 2) {
Warning("too many port attribute fields (more than 2) '%s'", attr);
attr = SERVER_PORT_DEFAULT;
} else {
switch (*attr_str) {
case 'S':
// S is the special case of SSL term
ink_assert(ssl_accept_file_descriptor == NO_FD);
ssl_accept_file_descriptor = fd;
continue;
case 'C':
attr = SERVER_PORT_COMPRESSED;
break;
case 'T':
attr = SERVER_PORT_BLIND_TUNNEL;
break;
case 'X':
case '=':
case '<':
case '>':
case '\0':
attr = SERVER_PORT_DEFAULT;
break;
default:
Warning("unknown port attribute '%s'", attr_str);
attr = SERVER_PORT_DEFAULT;
};
}
}
accept_array[accept_index].fd = fd;
accept_array[accept_index].type = attr;
accept_index++;
}
ink_assert(accept_index < list_entries + 1);
accept_array[accept_index].fd = NO_FD;
return accept_array;
}
#if defined(linux)
#include <sys/prctl.h>
#endif
static int
set_core_size(const char *name, RecDataT data_type, RecData data, void *opaque_token)
{
NOWARN_UNUSED(name);
NOWARN_UNUSED(data_type);
RecInt size = data.rec_int;
struct rlimit lim;
bool failed = false;
NOWARN_UNUSED(opaque_token);
if (getrlimit(RLIMIT_CORE, &lim) < 0) {
failed = true;
} else {
if (size < 0) {
lim.rlim_cur = lim.rlim_max;
} else {
lim.rlim_cur = (rlim_t) size;
}
if (setrlimit(RLIMIT_CORE, &lim) < 0) {
failed = true;
}
#if defined(linux)
#ifndef PR_SET_DUMPABLE
#define PR_SET_DUMPABLE 4
#endif
// bz57317
if (size != 0)
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif // linux check
}
if (failed == true) {
Warning("Failed to set Core Limit : %s", strerror(errno));
}
return 0;
}
static void
init_core_size()
{
bool found;
RecInt coreSize;
found = (RecGetRecordInt("proxy.config.core_limit", &coreSize) == REC_ERR_OKAY);
if (found == false) {
Warning("Unable to determine core limit");
} else {
RecData rec_temp;
rec_temp.rec_int = coreSize;
set_core_size(NULL, RECD_INT, rec_temp, NULL);
found = (TS_RegisterConfigUpdateFunc("proxy.config.core_limit", set_core_size, NULL) == REC_ERR_OKAY);
ink_assert(found);
}
}
#if TS_USE_POSIX_CAP
// Restore the effective capabilities that we need.
int
restoreCapabilities() {
int zret = 0; // return value.
cap_t cap_set = cap_get_proc(); // current capabilities
// Capabilities to restore.
cap_value_t cap_list[] = { CAP_NET_ADMIN, CAP_NET_BIND_SERVICE };
static int const CAP_COUNT = sizeof(cap_list)/sizeof(*cap_list);
cap_set_flag(cap_set, CAP_EFFECTIVE, CAP_COUNT, cap_list, CAP_SET);
zret = cap_set_proc(cap_set);
cap_free(cap_set);
return zret;
}
#endif
static void
adjust_sys_settings(void)
{
#if defined(linux)
struct rlimit lim;
int mmap_max = -1;
int fds_throttle = -1;
TS_ReadConfigInteger(mmap_max, "proxy.config.system.mmap_max");
if (mmap_max >= 0) {
mallopt(M_MMAP_MAX, mmap_max); /* INKqa10797: MALLOC_MMAP_MAX_=32768; export MALLOC_MMAP_MAX_ */
}
TS_ReadConfigInteger(fds_throttle, "proxy.config.net.connections_throttle");
if (!getrlimit(RLIMIT_NOFILE, &lim)) {
if (fds_throttle > (int) (lim.rlim_cur + THROTTLE_FD_HEADROOM)) {
lim.rlim_cur = (lim.rlim_max = (rlim_t) fds_throttle);
if (!setrlimit(RLIMIT_NOFILE, &lim) && !getrlimit(RLIMIT_NOFILE, &lim)) {
fds_limit = (int) lim.rlim_cur;
syslog(LOG_NOTICE, "NOTE: RLIMIT_NOFILE(%d):cur(%d),max(%d)",RLIMIT_NOFILE, (int)lim.rlim_cur, (int)lim.rlim_max);
}
}
}
set_rlimit(RLIMIT_STACK,true,true);
set_rlimit(RLIMIT_DATA,true,true);
set_rlimit(RLIMIT_FSIZE, true, false);
#ifdef RLIMIT_RSS
set_rlimit(RLIMIT_RSS,true,true);
#endif
#endif // linux check
#if TS_USE_POSIX_CAP
restoreCapabilities();
#endif
}
struct ShowStats: public Continuation
{
#ifdef ENABLE_TIME_TRACE
FILE *fp;
#endif
int cycle;
int64_t last_cc;
int64_t last_rb;
int64_t last_w;
int64_t last_r;
int64_t last_wb;
int64_t last_nrb;
int64_t last_nw;
int64_t last_nr;
int64_t last_nwb;
int64_t last_p;
int64_t last_o;
int mainEvent(int event, Event * e)
{
(void) event;
(void) e;
if (!(cycle++ % 24))
printf("r:rr w:ww r:rbs w:wbs open polls\n");
ink_statval_t sval, cval;
NET_READ_DYN_SUM(net_calls_to_readfromnet_stat, sval);
int64_t d_rb = sval - last_rb;
last_rb += d_rb;
NET_READ_DYN_SUM(net_calls_to_readfromnet_afterpoll_stat, sval);
int64_t d_r = sval - last_r;
last_r += d_r;
NET_READ_DYN_SUM(net_calls_to_writetonet_stat, sval);
int64_t d_wb = sval - last_wb;
last_wb += d_wb;
NET_READ_DYN_SUM(net_calls_to_writetonet_afterpoll_stat, sval);
int64_t d_w = sval - last_w;
last_w += d_w;
NET_READ_DYN_STAT(net_read_bytes_stat, sval, cval);
int64_t d_nrb = sval - last_nrb;
last_nrb += d_nrb;
int64_t d_nr = cval - last_nr;
last_nr += d_nr;
NET_READ_DYN_STAT(net_write_bytes_stat, sval, cval);
int64_t d_nwb = sval - last_nwb;
last_nwb += d_nwb;
int64_t d_nw = cval - last_nw;
last_nw += d_nw;
NET_READ_GLOBAL_DYN_SUM(net_connections_currently_open_stat, sval);
int64_t d_o = sval;
NET_READ_DYN_STAT(net_handler_run_stat, sval, cval);
int64_t d_p = cval - last_p;
last_p += d_p;
printf("%" PRId64 ":%" PRId64 " %" PRId64 ":%" PRId64 " %" PRId64 ":%" PRId64 " %" PRId64 ":%" PRId64 " %" PRId64 " %" PRId64 "\n",
d_rb, d_r, d_wb, d_w, d_nrb, d_nr, d_nwb, d_nw, d_o, d_p);
#ifdef ENABLE_TIME_TRACE
int i;
fprintf(fp, "immediate_events_time_dist\n");
for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++)
{
if ((i % 10) == 0)
fprintf(fp, "\n");
fprintf(fp, "%5d ", immediate_events_time_dist[i]);
}
fprintf(fp, "\ncnt_immediate_events=%d\n", cnt_immediate_events);
fprintf(fp, "cdb_callback_time_dist\n");
for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
if ((i % 10) == 0)
fprintf(fp, "\n");
fprintf(fp, "%5d ", cdb_callback_time_dist[i]);
}
fprintf(fp, "\ncdb_cache_callbacks=%d\n", cdb_cache_callbacks);
fprintf(fp, "callback_time_dist\n");
for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
if ((i % 10) == 0)
printf("\n");
fprintf(fp, "%5d ", callback_time_dist[i]);
}
fprintf(fp, "\ncache_callbacks=%d\n", cache_callbacks);
fprintf(fp, "rmt_callback_time_dist\n");
for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
if ((i % 10) == 0)
fprintf(fp, "\n");
fprintf(fp, "%5d ", rmt_callback_time_dist[i]);
}
fprintf(fp, "\nrmt_cache_callbacks=%d\n", rmt_cache_callbacks);
fprintf(fp, "inmsg_time_dist\n");
for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
if ((i % 10) == 0)
fprintf(fp, "\n");
fprintf(fp, "%5d ", inmsg_time_dist[i]);
}
fprintf(fp, "\ninmsg_events=%d\n", inmsg_events);
fprintf(fp, "open_delay_time_dist\n");
for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
if ((i % 10) == 0)
fprintf(fp, "\n");
fprintf(fp, "%5d ", open_delay_time_dist[i]);
}
fprintf(fp, "\nopen_delay_events=%d\n", open_delay_events);
fprintf(fp, "cluster_send_time_dist\n");
for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
if ((i % 10) == 0)
fprintf(fp, "\n");
fprintf(fp, "%5d ", cluster_send_time_dist[i]);
}
fprintf(fp, "\ncluster_send_events=%d\n", cluster_send_events);
fflush(fp);
#endif
return EVENT_CONT;
}
ShowStats():Continuation(NULL),
cycle(0),
last_cc(0),
last_rb(0),
last_w(0), last_r(0), last_wb(0), last_nrb(0), last_nw(0), last_nr(0), last_nwb(0), last_p(0), last_o(0) {
SET_HANDLER(&ShowStats::mainEvent);
#ifdef ENABLE_TIME_TRACE
fp = fopen("./time_trace.out", "a");
#endif
}
};
// TODO: How come this is never used ??
static int syslog_facility = LOG_DAEMON;
// static void syslog_log_configure()
//
// Reads the syslog configuration variable
// and sets the global integer for the
// facility and calls open log with the
// new facility
//
static void
syslog_log_configure()
{
char *facility_str = NULL;
int facility;
TS_ReadConfigStringAlloc(facility_str, "proxy.config.syslog_facility");
if (facility_str == NULL || (facility = facility_string_to_int(facility_str)) < 0) {
syslog(LOG_WARNING, "Bad or missing syslog facility. " "Defaulting to LOG_DAEMON");
} else {
syslog_facility = facility;
closelog();
openlog("traffic_server", LOG_PID | LOG_NDELAY | LOG_NOWAIT, facility);
}
}
// void syslog_thr_init()
//
// On the alpha, each thread must set its own syslog
// parameters. This function is to be called by
// each thread at start up. It inits syslog
// with stored facility information from system
// startup
//
void
syslog_thr_init()
{
}
static void
check_system_constants()
{
}
/*
static void
init_logging()
{
// iObject::Init();
// iLogBufferBuffer::Init();
}
*/
static void
init_http_header()
{
char internal_config_dir[PATH_NAME_MAX + 1];
ink_filepath_make(internal_config_dir, sizeof(internal_config_dir),
system_config_directory, "internal");
url_init(internal_config_dir);
mime_init(internal_config_dir);
http_init(internal_config_dir);
//extern void init_http_auth();
//init_http_auth();
}
static void
init_http_aeua_filter(void)
{
char buf[2048], _cname[1024], *cname;
int i, j;
cname = &_cname[0];
memset(buf, 0, sizeof(buf));
memset(_cname, 0, sizeof(_cname));
TS_ReadConfigString(_cname, "proxy.config.http.accept_encoding_filter.filename", (int) sizeof(_cname));
if (_cname[0] && (j = strlen(_cname)) > 0) {
while (j && (*cname == '/' || *cname == '\\')) {
++cname;
--j;
}
ink_strncpy(buf, system_config_directory, sizeof(buf));
if ((i = strlen(buf)) >= 0) {
if (!i || (buf[i - 1] != '/' && buf[i - 1] != '\\' && i < (int) sizeof(buf))) {
strncat(buf, "/", 1);
++i;
}
}
if ((i + j + 1) < (int) sizeof(buf))
strncat(buf, cname, sizeof(_cname) - 1);
}
i = HttpConfig::init_aeua_filter(buf[0] ? buf : NULL);
Debug("http_aeua", "[init_http_aeua_filter] - Total loaded %d REGEXP for Accept-Enconding/User-Agent filtering", i);
}
struct AutoStopCont: public Continuation
{
int mainEvent(int event, Event * e)
{
(void) event;
(void) e;
_exit(0);
return 0;
}
AutoStopCont():Continuation(new_ProxyMutex())
{
SET_HANDLER(&AutoStopCont::mainEvent);
}
};
static void
run_AutoStop()
{
if (getenv("PROXY_AUTO_EXIT"))
eventProcessor.schedule_in(NEW(new AutoStopCont), HRTIME_SECONDS(atoi(getenv("PROXY_AUTO_EXIT"))));
}
#if TS_HAS_TESTS
struct RegressionCont: public Continuation
{
int initialized;
int waits;
int started;
int mainEvent(int event, Event * e)
{
(void) event;
(void) e;
int res = 0;
if (!initialized && (cacheProcessor.IsCacheEnabled() != CACHE_INITIALIZED))
{
printf("Regression waiting for the cache to be ready... %d\n", ++waits);
return EVENT_CONT;
}
char *rt = (char *) (regression_test[0] == '\0' ? '\0' : regression_test);
if (!initialized && RegressionTest::run(rt) == REGRESSION_TEST_INPROGRESS) {
initialized = 1;
return EVENT_CONT;
}
if ((res = RegressionTest::check_status()) == REGRESSION_TEST_INPROGRESS)
return EVENT_CONT;
fprintf(stderr, "REGRESSION_TEST DONE: %s\n", regression_status_string(res));
_exit(res == REGRESSION_TEST_PASSED ? 0 : 1);
return EVENT_CONT;
}
RegressionCont():Continuation(new_ProxyMutex()), initialized(0), waits(0), started(0) {
SET_HANDLER(&RegressionCont::mainEvent);
}
};
static void
run_RegressionTest()
{
if (regression_level)
eventProcessor.schedule_every(NEW(new RegressionCont), HRTIME_SECONDS(1));
}
#endif //TS_HAS_TESTS
static void
chdir_root()
{
if (system_root_dir[0] && (chdir(system_root_dir) < 0)) {
fprintf(stderr,"unable to change to root directory \"%s\" [%d '%s']\n",
system_root_dir, errno, strerror(errno));
fprintf(stderr," please set correct path in env variable TS_ROOT \n");
_exit(1);
} else {
printf("[TrafficServer] using root directory '%s'\n", system_root_dir);
}
}
int
getNumSSLThreads(void)
{
int ssl_enabled = 0;
int config_num_ssl_threads = 0;
int ssl_blocking = 0;
TS_ReadConfigInteger(ssl_enabled, "proxy.config.ssl.enabled");
TS_ReadConfigInteger(config_num_ssl_threads, "proxy.config.ssl.number.threads");
TS_ReadConfigInteger(ssl_blocking, "proxy.config.ssl.accelerator.type");
// Set number of ssl threads equal to num of processors if
// SSL is enabled so it will scale properly. If an accelerator card
// is present, there will be blocking, so scale threads up. If SSL is not
// enabled, leave num of ssl threads one, incase a remap rule
// requires traffic server to act as an ssl client.
if (ssl_enabled) {
if (config_num_ssl_threads != 0)
num_of_ssl_threads = config_num_ssl_threads;
else if (ssl_blocking != 0)
num_of_ssl_threads = number_of_processors * 4;
else {
ink_assert(number_of_processors);
switch (number_of_processors) {
case 0:
break;
case 1:
case 2:
num_of_ssl_threads = number_of_processors;
break;
case 3:
case 4:
default:
num_of_ssl_threads = number_of_processors * 2;
break;
}
}
}
return (num_of_ssl_threads);
}
static void
adjust_num_of_net_threads(void)
{
float autoconfig_scale = 1.0;
int nth_auto_config = 1;
int num_of_threads_tmp = 1;
TS_ReadConfigInteger(nth_auto_config, "proxy.config.exec_thread.autoconfig");
if (!nth_auto_config) {
TS_ReadConfigInteger(num_of_threads_tmp, "proxy.config.exec_thread.limit");
if (num_of_threads_tmp <= 0)
num_of_threads_tmp = 1;
else if (num_of_threads_tmp > MAX_EVENT_THREADS)
num_of_threads_tmp = MAX_EVENT_THREADS;
num_of_net_threads = num_of_threads_tmp;
if (is_debug_tag_set("threads")) {
fprintf(stderr, "# net threads Auto config - disabled - use config file settings\n");
}
} else { /* autoconfig is enabled */
num_of_threads_tmp = num_of_net_threads;
TS_ReadConfigFloat(autoconfig_scale, "proxy.config.exec_thread.autoconfig.scale");
num_of_threads_tmp = (int) ((float) num_of_threads_tmp * autoconfig_scale);
if (num_of_threads_tmp) {
num_of_net_threads = num_of_threads_tmp;
}
if (unlikely(num_of_threads_tmp > MAX_EVENT_THREADS)) {
num_of_threads_tmp = MAX_EVENT_THREADS;
}
if (is_debug_tag_set("threads")) {
fprintf(stderr, "# net threads Auto config - enabled\n");
fprintf(stderr, "# autoconfig scale: %f\n", autoconfig_scale);
fprintf(stderr, "# scaled number of net threads: %d\n", num_of_threads_tmp);
}
}
if (is_debug_tag_set("threads")) {
fprintf(stderr, "# number of net threads: %d\n", num_of_net_threads);
}
if (unlikely(num_of_net_threads <= 0)) { /* impossible case -just for protection */
Warning("Number of Net Threads should be greater than 0");
num_of_net_threads = 1;
}
}
/**
* Change the uid and gid to what is in the passwd entry for supplied user name.
* @param user User name in the passwd file to change the uid and gid to.
*/
void
change_uid_gid(const char *user)
{
struct passwd pwbuf;
struct passwd *pwbufp = NULL;
#if defined(freebsd) // TODO: investigate sysconf(_SC_GETPW_R_SIZE_MAX)) failure
long buflen = 1024; // or 4096?
#else
long buflen = sysconf(_SC_GETPW_R_SIZE_MAX);
#endif
if (buflen < 0) {
ink_fatal_die("sysconf() failed for _SC_GETPW_R_SIZE_MAX");
}
char *buf = (char *)xmalloc(buflen);
if (geteuid()) {
// Not running as root
Debug("server",
"Can't change user to : %s because running with effective uid=%d",
user, geteuid());
}
else {
if (user[0] == '#') {
// numeric user notation
uid_t uid = (uid_t)atoi(&user[1]);
getpwuid_r(uid, &pwbuf, buf, buflen, &pwbufp);
}
else {
// read the entry from the passwd file
getpwnam_r(user, &pwbuf, buf, buflen, &pwbufp);
}
// check to see if we found an entry
if (pwbufp == NULL) {
ink_fatal_die("Can't find entry in password file for user: %s", user);
}
#if !defined (BIG_SECURITY_HOLE)
if (pwbuf.pw_uid == 0) {
ink_fatal_die("Trafficserver has not been designed to serve pages while\n"
"\trunning as root. There are known race conditions that\n"
"\twill allow any local user to read any file on the system.\n"
"\tIf you still desire to serve pages as root then\n"
"\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
"\tand then rebuild the server.\n"
"\tIt is strongly suggested that you instead modify the\n"
"\tproxy.config.admin.user_id directive in your\n"
"\trecords.config file to list a non-root user.\n");
}
#endif
// change the gid to passwd entry if we are not already running as that gid
if (getgid() != pwbuf.pw_gid) {
if (setgid(pwbuf.pw_gid) != 0) {
ink_fatal_die("Can't change group to user: %s, gid: %d",
user, pwbuf.pw_gid);
}
}
// change the uid to passwd entry if we are not already running as that uid
if (getuid() != pwbuf.pw_uid) {
if (setuid(pwbuf.pw_uid) != 0) {
ink_fatal_die("Can't change uid to user: %s, uid: %d",
user, pwbuf.pw_uid);
}
}
}
xfree(buf);
}
#if TS_HAS_V2STATS
void init_stat_collector()
{
static int stat_collection_interval;
static int stat_collector_port;
static int max_stats_allowed = 0;
static int num_stats_estimate = 0;
// Read config variables
TS_ReadConfigInteger(stat_collection_interval, "proxy.config.stat_collector.interval");
TS_ReadConfigInteger(stat_collector_port, "proxy.config.stat_collector.port");
TS_ReadConfigInteger(max_stats_allowed, "proxy.config.stat_systemV2.max_stats_allowed");
TS_ReadConfigInteger(num_stats_estimate, "proxy.config.stat_systemV2.num_stats_estimate");
// TODO: This seems unused
// TS_ReadConfigInteger(temp, "proxy.config.cache.threads_per_disk");
// Set to default if not defined in config file
if(!stat_collector_port) {
stat_collector_port = 8091;
}
if(!stat_collection_interval) {
stat_collection_interval = 600;
}
if(max_stats_allowed) {
StatSystemV2::setMaxStatsAllowed((uint32_t)max_stats_allowed);
}
if(num_stats_estimate) {
StatSystemV2::setNumStatsEstimate((uint32_t)num_stats_estimate);
}
StatSystemV2::init();
StatCollectorContinuation::setStatCommandPort(stat_collector_port);
eventProcessor.schedule_every(NEW (new StatCollectorContinuation()),
HRTIME_SECONDS(stat_collection_interval), ET_CALL);
}
#endif
//
// Main
//
int
main(int argc, char **argv)
{
#if TS_HAS_PROFILER
ProfilerStart("/tmp/ts.prof");
#endif
NOWARN_UNUSED(argc);
//init_logging();
#ifdef HAVE_MCHECK
mcheck_pedantic(NULL);
#endif
// Verify system dependent 'constants'
check_system_constants();
// Define the version info
appVersionInfo.setup(PACKAGE_NAME,"traffic_server", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
// Before accessing file system initialize Layout engine
Layout::create();
ink_strncpy(system_root_dir, Layout::get()->prefix, PATH_NAME_MAX);
ink_strncpy(management_directory, Layout::get()->sysconfdir, PATH_NAME_MAX);
chdir_root(); // change directory to the install root of traffic server.
process_args(argument_descriptions, n_argument_descriptions, argv);
// Check for version number request
if (version_flag) {
fprintf(stderr, "%s\n", appVersionInfo.FullVersionInfoStr);
_exit(0);
}
// Ensure only one copy of traffic server is running
check_lockfile();
// Set stdout/stdin to be unbuffered
setbuf(stdout, NULL);
setbuf(stdin, NULL);
// Set new debug output level (from command line arg)
// Only for debug purposes. We should do it as early as possible.
ink_set_dprintf_level(cmd_line_dprintf_level);
// Bootstrap syslog. Since we haven't read records.config
// yet we do not know where
openlog("traffic_server", LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON);
// Setup Diags temporary to allow librecords to be initialized.
// We will re-configure Diags again with proper configurations after
// librecords initialized. This is needed because:
// - librecords needs diags to initialize
// - diags needs to read some configuration records to initial
// We cannot mimic whatever TM did (start Diag, init. librecords, and
// re-start Diag completely) because at initialize, TM only has 1 thread.
// In TS, some threads have already created, so if we delete Diag and
// re-start it again, TS will crash.
diagsConfig = NEW(new DiagsConfig(error_tags, action_tags, false));
diags = diagsConfig->diags;
diags_init = 1;
diags->prefix_str = "Server ";
if (is_debug_tag_set("diags"))
diags->dump();
// Local process manager
initialize_process_manager();
// Set the core limit for the process
init_core_size();
init_system();
// Adjust system and process settings
adjust_sys_settings();
// Restart syslog now that we have configuration info
syslog_log_configure();
if (!num_accept_threads)
TS_ReadConfigInteger(num_accept_threads, "proxy.config.accept_threads");
if (!num_task_threads)
TS_ReadConfigInteger(num_task_threads, "proxy.config.task_threads");
// This call is required for win_9xMe
//without this this_ethread() is failing when
//start_HttpProxyServer is called from main thread
Thread *main_thread = NEW(new EThread);
main_thread->set_specific();
// Re-initialize diagsConfig based on records.config configuration
if (diagsConfig) {
RecDebugOff();
delete(diagsConfig);
}
diagsConfig = NEW(new DiagsConfig(error_tags, action_tags, true));
diags = diagsConfig->diags;
RecSetDiags(diags);
diags_init = 1;
diags->prefix_str = "Server ";
if (is_debug_tag_set("diags"))
diags->dump();
// Check for core file
if (core_file[0] != '\0') {
process_core(core_file);
_exit(0);
}
// pmgmt->start() must occur after initialization of Diags but
// before calling RecProcessInit()
TS_ReadConfigInteger(history_info_enabled, "proxy.config.history_info_enabled");
TS_ReadConfigInteger(res_track_memory, "proxy.config.res_track_memory");
{
XMLDom schema;
bool xmlBandwidthSchemaRead(XMLNode * node);
//char *configPath = TS_ConfigReadString("proxy.config.config_dir");
char *filename = TS_ConfigReadString("proxy.config.bandwidth_mgmt.filename");
char bwFilename[PATH_NAME_MAX];
snprintf(bwFilename, sizeof(bwFilename), "%s/%s", system_config_directory, filename);
Debug("bw-mgmt", "Looking to read: %s for bw-mgmt", bwFilename);
schema.LoadFile(bwFilename);
xmlBandwidthSchemaRead(&schema);
}
init_http_header();
// Init HTTP Accept-Encoding/User-Agent filter
init_http_aeua_filter();
// Parse the accept port list from the manager
http_port_attr_array = parse_accept_fd_list();
if (is_debug_tag_set("accept_fd"))
print_accept_fd(http_port_attr_array);
// Sanity checks
// if (!lock_process) check_for_root_uid();
check_fd_limit();
command_flag = command_flag || *command_string;
// Set up store
if (!command_flag && initialize_store())
ProcessFatal("unable to initialize storage, (Re)Configuration required\n");
// Read proxy name
TS_ReadConfigString(proxy_name, "proxy.config.proxy_name", 255);
// Initialize the stat pages manager
statPagesManager.init();
//////////////////////////////////////////////////////////////////////
// Determine if Cache Clustering is enabled, since the transaction
// on a thread changes require special consideration to allow
// minimial Cache Clustering functionality.
//////////////////////////////////////////////////////////////////////
int cluster_type = 0;
//kwt
//ReadLocalInteger(cluster_type, "proxy.config.cluster.type");
RecInt temp_int;
RecGetRecordInt("proxy.local.cluster.type", &temp_int);
cluster_type = (int) temp_int;
if (cluster_type == 1) {
cache_clustering_enabled = 1;
Note("cache clustering enabled");
} else {
cache_clustering_enabled = 0;
/* 3com does not want these messages to be seen */
Note("cache clustering disabled");
}
// Initialize New Stat system
initialize_all_global_stats();
adjust_num_of_net_threads();
ink_event_system_init(makeModuleVersion(1, 0, PRIVATE_MODULE_HEADER));
ink_net_init(makeModuleVersion(1, 0, PRIVATE_MODULE_HEADER));
ink_aio_init(makeModuleVersion(1, 0, PRIVATE_MODULE_HEADER));
ink_cache_init(makeModuleVersion(1, 0, PRIVATE_MODULE_HEADER));
ink_hostdb_init(makeModuleVersion(1, 0, PRIVATE_MODULE_HEADER));
ink_dns_init(makeModuleVersion(1, 0, PRIVATE_MODULE_HEADER));
ink_split_dns_init(makeModuleVersion(1, 0, PRIVATE_MODULE_HEADER));
eventProcessor.start(num_of_net_threads);
#if TS_HAS_V2STATS
// Must be called after starting event processor
init_stat_collector();
#endif
int use_separate_thread = 0;
int num_remap_threads = 1;
TS_ReadConfigInteger(use_separate_thread, "proxy.config.remap.use_remap_processor");
TS_ReadConfigInteger(num_remap_threads, "proxy.config.remap.num_remap_threads");
if (use_separate_thread && num_remap_threads < 1)
num_remap_threads = 1;
if (use_separate_thread) {
Note("using the new remap processor system with %d threads", num_remap_threads);
remapProcessor.setUseSeparateThread();
}
remapProcessor.start(num_remap_threads);
RecProcessStart();
init_signals2();
// log initialization moved down
if (command_flag) {
// pmgmt initialization moved up, needed by RecProcessInit
//pmgmt->start();
int cmd_ret = cmd_mode();
if (cmd_ret != CMD_IN_PROGRESS) {
if (cmd_ret >= 0)
_exit(0); // everything is OK
else
_exit(1); // in error
}
} else {
#ifndef INK_NO_ACL
initCacheControl();
#endif
initCongestionControl();
initIPAllow();
ParentConfig::startup();
#ifdef SPLIT_DNS
SplitDNSConfig::startup();
#endif
if (!accept_mss)
TS_ReadConfigInteger(accept_mss, "proxy.config.net.sock_mss_in");
NetProcessor::accept_mss = accept_mss;
netProcessor.start();
create_this_machine();
#ifndef INK_NO_HOSTDB
dnsProcessor.start();
if (hostDBProcessor.start() < 0)
SignalWarning(MGMT_SIGNAL_SYSTEM_ERROR, "bad hostdb or storage configuration, hostdb disabled");
#endif
#ifndef INK_NO_CLUSTER
clusterProcessor.init();
#endif
cacheProcessor.start();
udpNet.start(num_of_udp_threads); // XXX : broken for __WIN32
sslNetProcessor.start(getNumSSLThreads());
#ifndef INK_NO_LOG
// initialize logging (after event and net processor)
Log::init(remote_management_flag ? 0 : Log::NO_REMOTE_MANAGEMENT);
#endif
#if !defined(TS_NO_API)
plugin_init(system_config_directory, true); // extensions.config
#endif
//acc.init();
//if (auto_clear_authdb_flag)
// acc.clear_cache();
//acc.start();
// pmgmt initialization moved up, needed by RecProcessInit
//pmgmt->start();
start_stats_snap();
// Initialize Response Body Factory
body_factory = NEW(new HttpBodyFactory);
// Start IP to userName cache processor used
// by RADIUS and FW1 plug-ins.
//ipToUserNameCacheProcessor.start();
// Initialize the system for SIMPLE support
// Simple::init();
// Initialize the system for RAFT support
// All this is handled by plugin support code
// Raft::init();
// Continuation Statistics Dump
if (show_statistics)
eventProcessor.schedule_every(NEW(new ShowStats), HRTIME_SECONDS(show_statistics), ET_CALL);
/////////////////////////////////////////////
// if in test hook mode, run the test hook //
/////////////////////////////////////////////
#if TS_HAS_TESTS
if (run_test_hook) {
Note("Running TestHook Instead of Main Server");
run_TestHook();
}
#endif
//////////////////////////////////////
// main server logic initiated here //
//////////////////////////////////////
#ifndef TS_NO_API
plugin_init(system_config_directory, false); // plugin.config
#else
api_init(); // we still need to initialize some of the data structure other module needs.
extern void init_inkapi_stat_system();
init_inkapi_stat_system();
// i.e. http_global_hooks
#endif
#ifndef TS_NO_TRANSFORM
transformProcessor.start();
#endif
init_HttpProxyServer();
if (!http_accept_port_number) {
TS_ReadConfigInteger(http_accept_port_number, "proxy.config.http.server_port");
}
if ((unsigned int) http_accept_port_number >= 0xFFFF) {
ProcessFatal("\ncannot listen on port %d.\naccept port cannot be larger that 65535.\n"
"please check your Traffic Server configurations", http_accept_port_number);
return (1);
}
int http_enabled = 1;
TS_ReadConfigInteger(http_enabled, "proxy.config.http.enabled");
if (http_enabled) {
start_HttpProxyServer(http_accept_file_descriptor, http_accept_port_number, ssl_accept_file_descriptor, num_accept_threads);
int hashtable_enabled = 0;
TS_ReadConfigInteger(hashtable_enabled, "proxy.config.connection_collapsing.hashtable_enabled");
if (hashtable_enabled) {
cacheProcessor.hashtable_tracker.createHashTable();
}
}
#ifndef INK_NO_ICP
icpProcessor.start();
#endif
// "Task" processor, possibly with its own set of task threads
tasksProcessor.start(num_task_threads);
int back_door_port = NO_FD;
TS_ReadConfigInteger(back_door_port, "proxy.config.process_manager.mgmt_port");
if (back_door_port != NO_FD)
start_HttpProxyServerBackDoor(back_door_port, num_accept_threads > 0 ? 1 : 0); // One accept thread is enough
#ifndef INK_NO_SOCKS
if (netProcessor.socks_conf_stuff->accept_enabled) {
start_SocksProxy(netProcessor.socks_conf_stuff->accept_port);
}
#endif
///////////////////////////////////////////
// Initialize Scheduled Update subsystem
///////////////////////////////////////////
updateManager.start();
void *mgmt_restart_shutdown_callback(void *, char *, int data_len);
pmgmt->registerMgmtCallback(MGMT_EVENT_SHUTDOWN, mgmt_restart_shutdown_callback, NULL);
pmgmt->registerMgmtCallback(MGMT_EVENT_RESTART, mgmt_restart_shutdown_callback, NULL);
Note("traffic server running");
#if TS_HAS_TESTS
TransformTest::run();
#ifndef INK_NO_HOSTDB
run_HostDBTest();
#endif
// run_SimpleHttp();
run_RegressionTest();
#endif
run_AutoStop();
}
// change the user of the process
const long max_login = sysconf(_SC_LOGIN_NAME_MAX) <= 0 ? _POSIX_LOGIN_NAME_MAX : sysconf(_SC_LOGIN_NAME_MAX);
char *user = (char *)xmalloc(max_login);
*user = '\0';
if ((TS_ReadConfigString(user, "proxy.config.admin.user_id",
max_login) == REC_ERR_OKAY) &&
user[0] != '\0' &&
strcmp(user, "#-1")) {
change_uid_gid(user);
xfree(user);
}
Debug("server",
"running as uid=%u, gid=%u, effective uid=%u, gid=%u",
(unsigned)getuid(), (unsigned)getgid(),
(unsigned)geteuid(), (unsigned)getegid());
this_thread()->execute();
}
bool
xmlBandwidthSchemaRead(XMLNode * node)
{
XMLNode *child, *c2;
int i, j, k;
unsigned char *p;
char *ip;
// file doesn't exist
if (node->getNodeName() == NULL) {
// alloc 1-elt array to store stuff for best-effort traffic
G_inkPipeInfo.perPipeInfo = NEW(new InkSinglePipeInfo[1]);
G_inkPipeInfo.perPipeInfo[0].wt = 1.0;
G_inkPipeInfo.numPipes = 0;
G_inkPipeInfo.interfaceMbps = 0.0;
return true;
}
if (strcmp(node->getNodeName(), "interface") != 0) {
Debug("bw-mgmt", "Root node should be an interface tag!\n");
return false;
}
// First entry G_inkPipeInfo.perPipeInfo[0] is the one for "best-effort" traffic.
G_inkPipeInfo.perPipeInfo = NEW(new InkSinglePipeInfo[node->getChildCount() + 1]);
G_inkPipeInfo.perPipeInfo[0].wt = 1.0;
G_inkPipeInfo.numPipes = 0;
G_inkPipeInfo.reliabilityMbps = 1.0;
G_inkPipeInfo.interfaceMbps = 30.0;
for (i = 0; i < node->getChildCount(); i++) {
if ((child = node->getChildNode(i))) {
if (strcmp(child->getNodeName(), "pipe") == 0) {
G_inkPipeInfo.numPipes++;
for (k = 0; k < child->getChildCount(); k++) {
c2 = child->getChildNode(k);
for (int l = 0; l < c2->m_nACount; l++) {
if (strcmp(c2->m_pAList[l].pAName, "weight") == 0) {
G_inkPipeInfo.perPipeInfo[G_inkPipeInfo.numPipes].wt = atof(c2->m_pAList[l].pAValue);
G_inkPipeInfo.perPipeInfo[0].wt -= G_inkPipeInfo.perPipeInfo[G_inkPipeInfo.numPipes].wt;
} else if (strcmp(c2->m_pAList[l].pAName, "dest_ip") == 0) {
p = (unsigned char *) &(G_inkPipeInfo.perPipeInfo[G_inkPipeInfo.numPipes].destIP);
ip = c2->m_pAList[l].pAValue;
for (j = 0; j < 4; j++) {
p[j] = atoi(ip);
while (ip && *ip && (*ip != '.'))
ip++;
ip++;
}
}
}
}
} else if (strcmp(child->getNodeName(), "bandwidth") == 0) {
for (j = 0; j < child->m_nACount; j++) {
if (strcmp(child->m_pAList[j].pAName, "limit_mbps") == 0) {
G_inkPipeInfo.interfaceMbps = atof(child->m_pAList[j].pAValue);
} else if (strcmp(child->m_pAList[j].pAName, "reliability_mbps") == 0) {
G_inkPipeInfo.reliabilityMbps = atof(child->m_pAList[j].pAValue);
}
}
}
}
}
Debug("bw-mgmt", "Read in: limit_mbps = %lf\n", G_inkPipeInfo.interfaceMbps);
for (i = 0; i < G_inkPipeInfo.numPipes + 1; i++) {
G_inkPipeInfo.perPipeInfo[i].bwLimit =
(int64_t) (G_inkPipeInfo.perPipeInfo[i].wt * G_inkPipeInfo.interfaceMbps * 1024.0 * 1024.0);
p = (unsigned char *) &(G_inkPipeInfo.perPipeInfo[i].destIP);
Debug("bw-mgmt", "Pipe [%d]: wt = %lf, dest ip = %d.%d.%d.%d\n",
i, G_inkPipeInfo.perPipeInfo[i].wt, p[0], p[1], p[2], p[3]);
}
return true;
}
#if TS_HAS_TESTS
//////////////////////////////
// Unit Regresion Test Hook //
//////////////////////////////
#include "HdrTest.h"
REGRESSION_TEST(Hdrs) (RegressionTest * t, int atype, int *pstatus) {
HdrTest ht;
*pstatus = ht.go(t, atype);
return;
}
#endif
void *
mgmt_restart_shutdown_callback(void *, char *, int data_len)
{
NOWARN_UNUSED(data_len);
sync_cache_dir_on_shutdown();
return NULL;
}