blob: ac23d2101e51fa1b3ff3346b5ad4b66ae5cad0a3 [file] [log] [blame]
/** @file
Entry point to the traffic manager.
@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.
*/
#include "ink_config.h"
#include "ink_platform.h"
#include "ink_sys_control.h"
#include "Main.h"
#include "MgmtUtils.h"
#include "WebMgmtUtils.h"
#include "WebIntrMain.h"
#include "WebOverview.h"
#include "FileManager.h"
#include "I_Layout.h"
#include "I_Version.h"
#include "ink_syslog.h"
#include "ink_lockfile.h"
#include "Diags.h"
#include "DiagsConfig.h"
#include "URL.h"
#include "MIME.h"
#include "HTTP.h"
// Needs LibRecordsConfigInit()
#include "RecordsConfig.h"
#if defined(MGMT_API)
#include "TSControlMain.h"
#endif
#include "StatProcessor.h"
#include "P_RecLocal.h"
#include "P_RecCore.h"
#if TS_USE_POSIX_CAP
#include <sys/capability.h>
#endif
#include <grp.h>
#define FD_THROTTLE_HEADROOM (128 + 64) // TODO: consolidate with THROTTLE_FD_HEADROOM
#define DIAGS_LOG_FILENAME "manager.log"
#if defined(freebsd)
extern "C" int getpwnam_r(const char *name, struct passwd *result, char *buffer, size_t buflen, struct passwd **resptr);
#endif
static void extractConfigInfo(char *mgmt_path, const char *recs_conf, char *userName, int *fds_throttle);
LocalManager *lmgmt = NULL;
FileManager *configFiles;
StatProcessor *statProcessor; // Statistics Processors
AppVersionInfo appVersionInfo; // Build info for this application
int diags_init = 0;
static inkcoreapi DiagsConfig *diagsConfig;
static char debug_tags[1024] = "";
static char action_tags[1024] = "";
static bool proxy_on = true;
char mgmt_path[PATH_NAME_MAX + 1];
// By default, set the current directory as base
static const char *recs_conf = "records.config";
static int fds_limit;
// TODO: Use positive instead negative selection
// Thsis should just be #if defined(solaris)
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
static void SignalHandler(int sig, siginfo_t * t, void *f);
static void SignalAlrmHandler(int sig, siginfo_t * t, void *f);
#else
static void SignalHandler(int sig);
static void SignalAlrmHandler(int sig);
#endif
static volatile int sigHupNotifier = 0;
static volatile int sigUsr2Notifier = 0;
static void SigChldHandler(int sig);
static void
check_lockfile()
{
xptr<char> rundir(RecConfigReadRuntimeDir());
char lockfile[PATH_NAME_MAX];
int err;
pid_t holding_pid;
//////////////////////////////////////
// test for presence of server lock //
//////////////////////////////////////
Layout::relative_to(lockfile, sizeof(lockfile), rundir, SERVER_LOCK);
Lockfile server_lockfile(lockfile);
err = server_lockfile.Open(&holding_pid);
if (err == 1) {
server_lockfile.Close(); // no server running
} else {
char *reason = strerror(-err);
if (err == 0) {
// TODO: Add PID_FMT_T instead duplicating code just for printing
#if defined(solaris)
fprintf(stderr, "FATAL: Lockfile '%s' says server already running as PID %d\n", lockfile, (int)holding_pid);
#else
fprintf(stderr, "FATAL: Lockfile '%s' says server already running as PID %d\n", lockfile, holding_pid);
#endif
mgmt_elog(stderr, 0, "FATAL: Lockfile '%s' says server already running as PID %d\n", lockfile, holding_pid);
} else {
fprintf(stderr, "FATAL: Can't open server lockfile '%s' (%s)\n", lockfile, (reason ? reason : "Unknown Reason"));
mgmt_elog(stderr, 0, "FATAL: Can't open server lockfile '%s' (%s)\n",
lockfile, (reason ? reason : "Unknown Reason"));
}
exit(1);
}
///////////////////////////////////////////
// try to get the exclusive manager lock //
///////////////////////////////////////////
Layout::relative_to(lockfile, sizeof(lockfile), rundir, MANAGER_LOCK);
Lockfile manager_lockfile(lockfile);
err = manager_lockfile.Get(&holding_pid);
if (err != 1) {
char *reason = strerror(-err);
fprintf(stderr, "FATAL: Can't acquire manager lockfile '%s'", lockfile);
mgmt_elog(stderr, 0, "FATAL: Can't acquire manager lockfile '%s'", lockfile);
if (err == 0) {
#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
mgmt_elog(stderr, 0, " (Lock file held by process ID %d)\n", holding_pid);
} else if (reason) {
fprintf(stderr, " (%s)\n", reason);
mgmt_elog(stderr, 0, " (%s)\n", reason);
} else {
fprintf(stderr, "\n");
}
exit(1);
fprintf(stderr, "unable to acquire manager lock [%d]\n", -err);
exit(1);
}
}
static void
initSignalHandlers()
{
struct sigaction sigHandler, sigChldHandler, sigAlrmHandler;
sigset_t sigsToBlock;
// Set up the signal handler
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
sigHandler.sa_handler = NULL;
sigHandler.sa_sigaction = SignalHandler;
#else
sigHandler.sa_handler = SignalHandler;
#endif
sigemptyset(&sigHandler.sa_mask);
// We want the handler to remain in place on
// SIGHUP to avoid any races with the signals
// coming too quickly. Also restart systems calls
// after the signal since not all calls are wrapped
// to check errno for EINTR
sigHandler.sa_flags = SA_RESTART;
sigaction(SIGHUP, &sigHandler, NULL);
sigaction(SIGUSR2, &sigHandler, NULL);
// Don't block the signal on entry to the signal
// handler so we can reissue it and get a core
// file in the appropriate circumstances
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
sigHandler.sa_flags = SA_RESETHAND | SA_SIGINFO;
#else
sigHandler.sa_flags = SA_RESETHAND;
#endif
sigaction(SIGINT, &sigHandler, NULL);
sigaction(SIGQUIT, &sigHandler, NULL);
sigaction(SIGILL, &sigHandler, NULL);
sigaction(SIGBUS, &sigHandler, NULL);
sigaction(SIGSEGV, &sigHandler, NULL);
sigaction(SIGTERM, &sigHandler, NULL);
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
sigAlrmHandler.sa_handler = NULL;
sigAlrmHandler.sa_sigaction = SignalAlrmHandler;
#else
sigAlrmHandler.sa_handler = SignalAlrmHandler;
#endif
sigemptyset(&sigAlrmHandler.sa_mask);
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
sigAlrmHandler.sa_flags = SA_SIGINFO;
#else
sigAlrmHandler.sa_flags = 0;
#endif
sigaction(SIGALRM, &sigAlrmHandler, NULL);
// Block the delivery of any signals we are not catching
//
// except for SIGALARM since we use it
// to break out of deadlock on semaphore
// we share with the proxy
//
sigfillset(&sigsToBlock);
sigdelset(&sigsToBlock, SIGHUP);
sigdelset(&sigsToBlock, SIGUSR2);
sigdelset(&sigsToBlock, SIGINT);
sigdelset(&sigsToBlock, SIGQUIT);
sigdelset(&sigsToBlock, SIGILL);
sigdelset(&sigsToBlock, SIGABRT);
sigdelset(&sigsToBlock, SIGBUS);
sigdelset(&sigsToBlock, SIGSEGV);
sigdelset(&sigsToBlock, SIGTERM);
sigdelset(&sigsToBlock, SIGALRM);
ink_thread_sigsetmask(SIG_SETMASK, &sigsToBlock, NULL);
// Set up the SIGCHLD handler so we do not get into
// a problem with Solaris 2.6 and strange waitpid()
// behavior
sigChldHandler.sa_handler = SigChldHandler;
sigChldHandler.sa_flags = SA_RESTART;
sigemptyset(&sigChldHandler.sa_mask);
sigaction(SIGCHLD, &sigChldHandler, NULL);
}
#if defined(linux)
#include <sys/prctl.h>
#endif
static int
setup_coredump()
{
#if defined(linux)
#ifndef PR_SET_DUMPABLE
#define PR_SET_DUMPABLE 4 /* Ugly, but we cannot compile with 2.2.x otherwise.
Should be removed when we compile only on 2.4.x */
#endif
prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
#endif // linux check
return 0;
}
static void
init_dirs()
{
xptr<char> rundir(RecConfigReadRuntimeDir());
if (access(Layout::get()->sysconfdir, R_OK) == -1) {
mgmt_elog(0, "unable to access() config dir '%s': %d, %s\n", Layout::get()->sysconfdir, errno, strerror(errno));
mgmt_elog(0, "please set the 'TS_ROOT' environment variable\n");
_exit(1);
}
if (access(rundir, R_OK) == -1) {
mgmt_elog(0, "unable to access() local state dir '%s': %d, %s\n", (const char *)rundir, errno, strerror(errno));
mgmt_elog(0, "please set 'proxy.config.local_state_dir'\n");
_exit(1);
}
}
static void
chdir_root()
{
const char * prefix = Layout::get()->prefix;
if (chdir(prefix) < 0) {
mgmt_elog(0, "unable to change to root directory \"%s\" [%d '%s']\n", prefix, errno, strerror(errno));
mgmt_elog(0, " please set correct path in env variable TS_ROOT \n");
exit(1);
} else {
mgmt_log("[TrafficManager] using root directory '%s'\n", prefix);
}
}
static void
set_process_limits(int fds_throttle)
{
struct rlimit lim;
// Set needed rlimits (root)
ink_max_out_rlimit(RLIMIT_NOFILE, true, false);
ink_max_out_rlimit(RLIMIT_STACK, true, true);
ink_max_out_rlimit(RLIMIT_DATA, true, true);
ink_max_out_rlimit(RLIMIT_FSIZE, true, false);
#ifdef RLIMIT_RSS
ink_max_out_rlimit(RLIMIT_RSS, true, true);
#endif
if (!getrlimit(RLIMIT_NOFILE, &lim)) {
if (fds_throttle > (int) (lim.rlim_cur + FD_THROTTLE_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;
#ifdef MGMT_USE_SYSLOG
syslog(LOG_NOTICE, "NOTE: RLIMIT_NOFILE(%d):cur(%d),max(%d)",RLIMIT_NOFILE, (int)lim.rlim_cur, (int)lim.rlim_max);
#endif
}
}
}
}
#if TS_HAS_WCCP
static void
Errata_Logger(ts::Errata const& err) {
size_t n;
static size_t const SIZE = 4096;
char buff[SIZE];
if (err.size()) {
ts::Errata::Code code = err.top().getCode();
n = err.write(buff, SIZE, 1, 0, 2, "> ");
// strip trailing newlines.
while (n && (buff[n-1] == '\n' || buff[n-1] == '\r'))
buff[--n] = 0;
// log it.
if (code > 1) mgmt_elog(0, "[WCCP]%s", buff);
else if (code > 0) mgmt_log("[WCCP]%s", buff);
else Debug("WCCP", "%s", buff);
}
}
static void
Init_Errata_Logging() {
ts::Errata::registerSink(&Errata_Logger);
}
#endif
int
main(int argc, char **argv)
{
// Before accessing file system initialize Layout engine
Layout::create();
ink_strlcpy(mgmt_path, Layout::get()->sysconfdir, sizeof(mgmt_path));
// change the directory to the "root" directory
chdir_root();
// Line buffer standard output & standard error
int status;
status = setvbuf(stdout, NULL, _IOLBF, 0);
if (status != 0)
perror("WARNING: can't line buffer stdout");
status = setvbuf(stderr, NULL, _IOLBF, 0);
if (status != 0)
perror("WARNING: can't line buffer stderr");
bool found = false;
int just_started = 0;
int cluster_mcport = -1, cluster_rsport = -1;
// TODO: This seems completely incomplete, disabled for now
// int dump_config = 0, dump_process = 0, dump_node = 0, dump_cluster = 0, dump_local = 0;
char* proxy_port = 0;
int proxy_backdoor = -1;
char *envVar = NULL, *group_addr = NULL, *tsArgs = NULL;
bool log_to_syslog = true;
char userToRunAs[80];
int fds_throttle = -1;
time_t ticker;
ink_thread webThrId;
// Set up the application version info
appVersionInfo.setup(PACKAGE_NAME,"traffic_manager", PACKAGE_VERSION,
__DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
initSignalHandlers();
// Process Environment Variables
if ((envVar = getenv("MGMT_ACONF_PORT")) != NULL) {
aconf_port_arg = atoi(envVar);
}
if ((envVar = getenv("MGMT_CLUSTER_MC_PORT")) != NULL) {
cluster_mcport = atoi(envVar);
}
if ((envVar = getenv("MGMT_CLUSTER_RS_PORT")) != NULL) {
cluster_rsport = atoi(envVar);
}
if ((envVar = getenv("MGMT_GROUP_ADDR")) != NULL) {
group_addr = envVar;
}
for (int i = 1; i < argc; i++) { /* Process command line args */
if (argv[i][0] == '-') {
if ((strcmp(argv[i], "-version") == 0) || (strcmp(argv[i], "-V") == 0)) {
fprintf(stderr, "%s\n", appVersionInfo.FullVersionInfoStr);
exit(0);
} else if (strcmp(argv[i], "-proxyOff") == 0) {
proxy_on = false;
} else if (strcmp(argv[i], "-nosyslog") == 0) {
log_to_syslog = false;
} else {
// The rest of the options require an argument in the form of -<Flag> <val>
if ((i + 1) < argc) {
if (strcmp(argv[i], "-aconfPort") == 0) {
++i;
aconf_port_arg = atoi(argv[i]);
} else if (strcmp(argv[i], "-clusterMCPort") == 0) {
++i;
cluster_mcport = atoi(argv[i]);
} else if (strcmp(argv[i], "-groupAddr") == 0) {
++i;
group_addr = argv[i];
} else if (strcmp(argv[i], "-clusterRSPort") == 0) {
++i;
cluster_rsport = atoi(argv[i]);
#if TS_USE_DIAGS
} else if (strcmp(argv[i], "-debug") == 0) {
++i;
ink_strlcpy(debug_tags, argv[i], sizeof(debug_tags));
} else if (strcmp(argv[i], "-action") == 0) {
++i;
ink_strlcpy(action_tags, argv[i], sizeof(debug_tags));
#endif
} else if (strcmp(argv[i], "-path") == 0) {
++i;
//bugfixed by YTS Team, yamsat(id-59703)
if ((strlen(argv[i]) > PATH_NAME_MAX)) {
fprintf(stderr, "\n Path exceeded the maximum allowed characters.\n");
exit(1);
}
ink_strlcpy(mgmt_path, argv[i], sizeof(mgmt_path));
/*
} else if(strcmp(argv[i], "-lmConf") == 0) {
++i;
lm_conf = argv[i];
*/
} else if (strcmp(argv[i], "-recordsConf") == 0) {
++i;
recs_conf = argv[i];
// TODO: This seems completely incomplete, disabled for now
#if 0
} else if (strcmp(argv[i], "-printRecords") == 0) {
++i;
while (i < argc && argv[i][0] != '-') {
if (strcasecmp(argv[i], "config") == 0) {
dump_config = 1;
} else if (strcasecmp(argv[i], "process") == 0) {
dump_process = 1;
} else if (strcasecmp(argv[i], "node") == 0) {
dump_node = 1;
} else if (strcasecmp(argv[i], "cluster") == 0) {
dump_cluster = 1;
} else if (strcasecmp(argv[i], "local") == 0) {
dump_local = 1;
} else if (strcasecmp(argv[i], "all") == 0) {
dump_config = dump_node = dump_process = dump_cluster = dump_local = 1;
}
++i;
}
--i;
#endif
} else if (strcmp(argv[i], "-tsArgs") == 0) {
int size_of_args = 0, j = (++i);
while (j < argc) {
size_of_args += 1;
size_of_args += strlen((argv[j++]));
}
tsArgs = (char *)ats_malloc(size_of_args + 1);
j = 0;
while (i < argc) {
snprintf(&tsArgs[j], ((size_of_args + 1) - j), " %s", argv[i]);
j += strlen(argv[i]) + 1;
++i;
}
} else if (strcmp(argv[i], "-proxyPort") == 0) {
++i;
proxy_port = argv[i];
} else if (strcmp(argv[i], "-proxyBackDoor") == 0) {
++i;
proxy_backdoor = atoi(argv[i]);
} else {
printUsage();
}
} else {
printUsage();
}
}
}
}
#ifdef MGMT_USE_SYSLOG
// Bootstrap with LOG_DAEMON until we've read our configuration
if (log_to_syslog) {
openlog("traffic_manager", LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON);
mgmt_use_syslog();
syslog(LOG_NOTICE, "NOTE: --- Manager Starting ---");
syslog(LOG_NOTICE, "NOTE: Manager Version: %s", appVersionInfo.FullVersionInfoStr);
}
#endif /* MGMT_USE_SYSLOG */
// Bootstrap the Diags facility so that we can use it while starting
// up the manager
diagsConfig = NEW(new DiagsConfig(DIAGS_LOG_FILENAME, debug_tags, action_tags, false));
diags = diagsConfig->diags;
diags->prefix_str = "Manager ";
RecLocalInit();
LibRecordsConfigInit();
RecordsConfigOverrideFromEnvironment();
init_dirs();// setup critical directories, needs LibRecords
// Get the config info we need while we are still root
extractConfigInfo(mgmt_path, recs_conf, userToRunAs, &fds_throttle);
set_process_limits(fds_throttle); // as root
runAsUser(userToRunAs);
setup_coredump();
check_lockfile();
url_init();
mime_init();
http_init();
#if defined(MGMT_API)
// initialize alarm queue
int ret;
ret = init_mgmt_alarm_q(mgmt_alarm_event_q);
if (ret < 0)
mgmt_alarm_event_q = NULL;
#endif
#if TS_HAS_WCCP
Init_Errata_Logging();
#endif
ts_host_res_global_init();
lmgmt = new LocalManager(proxy_on);
RecLocalInitMessage();
lmgmt->initAlarm();
if (diags) {
delete diagsConfig;
// diagsConfig->reconfigure_diags(); INKqa11968
/*
delete diags;
diags = NEW (new Diags(debug_tags,action_tags));
*/
}
// INKqa11968: need to set up callbacks and diags data structures
// using configuration in records.config
diagsConfig = NEW(new DiagsConfig(DIAGS_LOG_FILENAME, debug_tags, action_tags, true));
diags = diagsConfig->diags;
RecSetDiags(diags);
diags->prefix_str = "Manager ";
if (is_debug_tag_set("diags"))
diags->dump();
diags->cleanup_func = mgmt_cleanup;
diags_init = 1;
// Setup the exported manager version records.
RecSetRecordString("proxy.node.version.manager.short", appVersionInfo.VersionStr);
RecSetRecordString("proxy.node.version.manager.long", appVersionInfo.FullVersionInfoStr);
RecSetRecordString("proxy.node.version.manager.build_number", appVersionInfo.BldNumStr);
RecSetRecordString("proxy.node.version.manager.build_time", appVersionInfo.BldTimeStr);
RecSetRecordString("proxy.node.version.manager.build_date", appVersionInfo.BldDateStr);
RecSetRecordString("proxy.node.version.manager.build_machine", appVersionInfo.BldMachineStr);
RecSetRecordString("proxy.node.version.manager.build_person", appVersionInfo.BldPersonStr);
// RecSetRecordString("proxy.node.version.manager.build_compile_flags",
// appVersionInfo.BldCompileFlagsStr);
#ifdef MGMT_USE_SYSLOG
if (log_to_syslog) {
char sys_var[] = "proxy.config.syslog_facility";
char *facility_str = NULL;
int facility_int;
facility_str = REC_readString(sys_var, &found);
ink_assert(found);
if (!found) {
mgmt_elog(0, "Could not read %s. Defaulting to DAEMON\n", sys_var);
facility_int = LOG_DAEMON;
} else {
facility_int = facility_string_to_int(facility_str);
ats_free(facility_str);
if (facility_int < 0) {
mgmt_elog(0, "Bad syslog facility specified. Defaulting to DAEMON\n");
facility_int = LOG_DAEMON;
}
}
// NOTE: do NOT call closelog() here. Solaris gets confused
// and some how it hoses later calls to readdir_r.
openlog("traffic_manager", LOG_PID | LOG_NDELAY | LOG_NOWAIT, facility_int);
lmgmt->syslog_facility = facility_int;
} else {
lmgmt->syslog_facility = -1;
}
#endif /* MGMT_USE_SYSLOG */
// Find out our hostname so we can use it as part of the initialization
setHostnameVar();
// Create the data structure for overview page
// Do this before the rest of the set up since it needs
// to created to handle any alarms thrown by later
// initialization
overviewGenerator = new overviewPage();
// Initialize the Config Object bindings before
// starting any other threads
configFiles = new FileManager();
initializeRegistry();
configFiles->registerCallback(fileUpdated);
// RecLocal's 'sync_thr' depends on 'configFiles', so we can't
// stat the 'sync_thr' until 'configFiles' has been initialized.
RecLocalStart();
/* Update cmd line overrides/environmental overrides/etc */
if (tsArgs) { /* Passed command line args for proxy */
ats_free(lmgmt->proxy_options);
lmgmt->proxy_options = tsArgs;
mgmt_log(stderr, "[main] Traffic Server Args: '%s'\n", lmgmt->proxy_options);
}
if (proxy_port) {
HttpProxyPort::loadValue(lmgmt->m_proxy_ports, proxy_port);
}
if (proxy_backdoor != -1) {
RecSetRecordInt("proxy.config.process_manager.mgmt_port", proxy_backdoor);
}
if (cluster_rsport == -1) {
cluster_rsport = REC_readInteger("proxy.config.cluster.rsport", &found);
ink_assert(found);
}
if (cluster_mcport == -1) {
cluster_mcport = REC_readInteger("proxy.config.cluster.mcport", &found);
ink_assert(found);
}
if (!group_addr) {
group_addr = REC_readString("proxy.config.cluster.mc_group_addr", &found);
ink_assert(found);
}
in_addr_t min_ip = inet_network("224.0.0.255");
in_addr_t max_ip = inet_network("239.255.255.255");
in_addr_t group_addr_ip = inet_network(group_addr);
if (!(min_ip < group_addr_ip && group_addr_ip < max_ip)) {
mgmt_fatal(0, "[TrafficManager] Multi-Cast group addr '%s' is not in the permitted range of %s\n",
group_addr, "224.0.1.0 - 239.255.255.255");
}
/* TODO: Do we really need to init cluster communication? */
lmgmt->initCCom(cluster_mcport, group_addr, cluster_rsport); /* Setup cluster communication */
lmgmt->initMgmtProcessServer(); /* Setup p-to-p process server */
// Now that we know our cluster ip address, add the
// UI record for this machine
overviewGenerator->addSelfRecord();
lmgmt->listenForProxy();
//
// As listenForProxy() may change/restore euid, we should put
// the creation of webIntr_main thread after it. So that we
// can keep a consistent euid when create mgmtapi/eventapi unix
// sockets in webIntr_main thread.
//
webThrId = ink_thread_create(webIntr_main, NULL); /* Spin web agent thread */
Debug("lm", "Created Web Agent thread (%" PRId64 ")", (int64_t)webThrId);
ticker = time(NULL);
mgmt_log("[TrafficManager] Setup complete\n");
statProcessor = NEW(new StatProcessor());
for (;;) {
lmgmt->processEventQueue();
lmgmt->pollMgmtProcessServer();
// Check for a SIGHUP
if (sigHupNotifier != 0) {
mgmt_log(stderr, "[main] Reading Configuration Files due to SIGHUP\n");
configFiles->rereadConfig();
lmgmt->signalEvent(MGMT_EVENT_PLUGIN_CONFIG_UPDATE, "*");
sigHupNotifier = 0;
mgmt_log(stderr, "[main] Reading Configuration Files Reread\n");
}
// Check for SIGUSR2
if (sigUsr2Notifier != 0) {
ink_stack_trace_dump();
sigUsr2Notifier = 0;
}
lmgmt->ccom->generateClusterDelta();
if (lmgmt->run_proxy && lmgmt->processRunning()) {
lmgmt->ccom->sendSharedData();
lmgmt->virt_map->lt_runGambit();
} else {
if (!lmgmt->run_proxy) { /* Down if we are not going to start another immed. */
/* Proxy is not up, so no addrs should be */
lmgmt->virt_map->downOurAddrs();
}
/* Proxy is not up, but we should still exchange config and alarm info */
lmgmt->ccom->sendSharedData(false);
}
lmgmt->ccom->checkPeers(&ticker);
overviewGenerator->checkForUpdates();
if (statProcessor) {
statProcessor->processStat();
}
if (lmgmt->mgmt_shutdown_outstanding == true) {
lmgmt->mgmtShutdown(true);
_exit(0);
}
if (lmgmt->run_proxy && !lmgmt->processRunning()) { /* Make sure we still have a proxy up */
if (lmgmt->startProxy())
just_started = 0;
else
just_started++;
} else { /* Give the proxy a chance to fire up */
just_started++;
}
/* This will catch the case were the proxy dies before it can connect to manager */
if (lmgmt->proxy_launch_outstanding && !lmgmt->processRunning() && just_started >= 120) {
just_started = 0;
lmgmt->proxy_launch_outstanding = false;
if (lmgmt->proxy_launch_pid != -1) {
int res;
kill(lmgmt->proxy_launch_pid, 9);
waitpid(lmgmt->proxy_launch_pid, &res, 0);
if (WIFSIGNALED(res)) {
int sig = WTERMSIG(res);
#ifdef NEED_PSIGNAL
mgmt_log(stderr, "[main] Proxy terminated due to Sig %d\n", sig);
#else
mgmt_log(stderr, "[main] Proxy terminated due to Sig %d: %s\n", sig, strsignal(sig));
#endif /* NEED_PSIGNAL */
}
}
mgmt_log(stderr, "[main] Proxy launch failed, retrying...\n");
}
}
if (statProcessor) {
delete(statProcessor);
}
#ifndef MGMT_SERVICE
return 0;
#endif
} /* End main */
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
static void
SignalAlrmHandler(int /* sig ATS_UNUSED */, siginfo_t * t, void * /* c ATS_UNUSED */)
#else
static void
SignalAlrmHandler(int /* sig ATS_UNUSED */)
#endif
{
/*
fprintf(stderr, "[TrafficManager] ==> SIGALRM received\n");
mgmt_elog(stderr, 0, "[TrafficManager] ==> SIGALRM received\n");
*/
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
if (t) {
if (t->si_code <= 0) {
#if defined(solaris)
fprintf(stderr, "[TrafficManager] ==> User Alarm from pid: %d uid: %d\n", (int)t->si_pid, t->si_uid);
#else
fprintf(stderr, "[TrafficManager] ==> User Alarm from pid: %d uid: %d\n", t->si_pid, t->si_uid);
#endif
mgmt_elog(stderr, 0, "[TrafficManager] ==> User Alarm from pid: %d uid: %d\n", t->si_pid, t->si_uid);
} else {
fprintf(stderr, "[TrafficManager] ==> Kernel Alarm Reason: %d\n", t->si_code);
mgmt_elog(stderr, 0, "[TrafficManager] ==> Kernel Alarm Reason: %d\n", t->si_code);
}
}
#endif
return;
}
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
static void
SignalHandler(int sig, siginfo_t * t, void *c)
#else
static void
SignalHandler(int sig)
#endif
{
static int clean = 0;
int status;
#if !defined(linux) && !defined(freebsd) && !defined(darwin)
if (t) {
if (t->si_code <= 0) {
#if defined(solaris)
fprintf(stderr, "[TrafficManager] ==> User Sig %d from pid: %d uid: %d\n", sig, (int)t->si_pid, t->si_uid);
#else
fprintf(stderr, "[TrafficManager] ==> User Sig %d from pid: %d uid: %d\n", sig, t->si_pid, t->si_uid);
#endif
mgmt_elog(stderr, 0, "[TrafficManager] ==> User Sig %d from pid: %d uid: %d\n", sig, t->si_pid, t->si_uid);
} else {
fprintf(stderr, "[TrafficManager] ==> Kernel Sig %d; Reason: %d\n", sig, t->si_code);
mgmt_elog(stderr, 0, "[TrafficManager] ==> Kernel Sig %d; Reason: %d\n", sig, t->si_code);
}
}
#endif
if (sig == SIGHUP) {
sigHupNotifier = 1;
return;
}
if (sig == SIGUSR2) {
sigUsr2Notifier = 1;
return;
}
fprintf(stderr, "[TrafficManager] ==> Cleaning up and reissuing signal #%d\n", sig);
mgmt_elog(stderr, 0, "[TrafficManager] ==> Cleaning up and reissuing signal #%d\n", sig);
if (lmgmt && !clean) {
clean = 1;
if (lmgmt->watched_process_pid != -1) {
if (sig == SIGTERM || sig == SIGINT) {
kill(lmgmt->watched_process_pid, sig);
waitpid(lmgmt->watched_process_pid, &status, 0);
}
}
lmgmt->mgmtCleanup();
}
switch (sig) {
case SIGQUIT:
case SIGILL:
case SIGTRAP:
#if !defined(linux)
case SIGEMT:
case SIGSYS:
#endif
case SIGFPE:
case SIGBUS:
case SIGSEGV:
case SIGXCPU:
case SIGXFSZ:
abort();
default:
fprintf(stderr, "[TrafficManager] ==> signal #%d\n", sig);
mgmt_elog(stderr, 0, "[TrafficManager] ==> signal #%d\n", sig);
_exit(sig);
}
fprintf(stderr, "[TrafficManager] ==> signal2 #%d\n", sig);
mgmt_elog(stderr, 0, "[TrafficManager] ==> signal2 #%d\n", sig);
_exit(sig);
} /* End SignalHandler */
// void SigChldHandler(int sig)
//
// An empty handler needed so that we catch SIGCHLD
// With Solaris 2.6, ignoring sig child changes the behavior
// of waitpid() so that if there are no unwaited children,
// waitpid() blocks until all child are transformed into
// zombies which is bad for us
//
static void
SigChldHandler(int /* sig ATS_UNUSED */)
{
}
void
printUsage()
{
fprintf(stderr, "----------------------------------------------------------------------------\n");
fprintf(stderr, " Traffic Manager Usage: (all args are optional)\n");
fprintf(stderr, "\n");
fprintf(stderr, " traffic_manager [options]\n");
fprintf(stderr, " -proxyPort <port> Port to have proxy listen on, overrides records.config.\n");
/* Function is currently #ifdef'ed out so no reason to advertise
fprintf(stderr,
" -proxyBackdoor <port> Port to put proxy mgmt port on.\n");
*/
/* Commented out because this option is used for debugging only.
fprintf(stderr,
" -noProxy Do not launch the proxy process.\n");
*/
fprintf(stderr, " -tsArgs [...] Args to proxy, everything till eol is passed.\n");
fprintf(stderr, " -webPort <port> Port for web interface.\n");
/*
fprintf(stderr,
" -graphPort <port> Port for dynamic graphs.\n");
*/
fprintf(stderr, " -clusterPort <port> Cluster Multicast port\n");
fprintf(stderr, " -groupAddr <addr> Cluster Multicast group, example: \"225.0.0.37\".\n");
fprintf(stderr, " -clusterRSPort <port> Cluster Multicast port.\n");
fprintf(stderr, " -path <path> Root path for config files.\n");
/*
fprintf(stderr,
" -lmConf <fname> Local Management config file.\n");
*/
fprintf(stderr, " -recordsConf <fname> General config file.\n");
// TODO: This seems completely incomplete, disabled for now
// fprintf(stderr, " -printRecords [...] Print flags, default all are off.\n");
fprintf(stderr, " -debug <tags> Enable the given debug tags\n");
fprintf(stderr, " -action <tags> Enable the given action tags.\n");
fprintf(stderr, " -version or -V Print version id and exit.\n");
fprintf(stderr, "\n");
fprintf(stderr, " [...] can be one+ of: [config process node cluster local all]\n");
fprintf(stderr, "----------------------------------------------------------------------------\n");
exit(0);
} /* End printUsage */
void
fileUpdated(char *fname, bool incVersion)
{
if (strcmp(fname, "cluster.config") == 0) {
lmgmt->signalFileChange("proxy.config.cluster.cluster_configuration");
} else if (strcmp(fname, "remap.config") == 0) {
lmgmt->signalFileChange("proxy.config.url_remap.filename");
} else if (strcmp(fname, "socks.config") == 0) {
lmgmt->signalFileChange("proxy.config.socks.socks_config_file");
} else if (strcmp(fname, "records.config") == 0) {
lmgmt->signalFileChange("records.config", incVersion);
} else if (strcmp(fname, "cache.config") == 0) {
lmgmt->signalFileChange("proxy.config.cache.control.filename");
} else if (strcmp(fname, "parent.config") == 0) {
lmgmt->signalFileChange("proxy.config.http.parent_proxy.file");
} else if (strcmp(fname, "ip_allow.config") == 0) {
lmgmt->signalFileChange("proxy.config.cache.ip_allow.filename");
} else if (strcmp(fname, "vaddrs.config") == 0) {
mgmt_log(stderr, "[fileUpdated] vaddrs.config updated\n");
lmgmt->virt_map->lt_readAListFile(fname);
} else if (strcmp(fname, "storage.config") == 0) {
mgmt_log(stderr, "[fileUpdated] storage.config changed, need restart auto-rebuild mode\n");
} else if (strcmp(fname, "proxy.pac") == 0) {
mgmt_log(stderr, "[fileUpdated] proxy.pac file has been modified\n");
} else if (strcmp(fname, "icp.config") == 0) {
lmgmt->signalFileChange("proxy.config.icp.icp_configuration");
} else if (strcmp(fname, "update.config") == 0) {
lmgmt->signalFileChange("proxy.config.update.update_configuration");
} else if (strcmp(fname, "volume.config") == 0) {
mgmt_log(stderr, "[fileUpdated] volume.config changed, need restart\n");
} else if (strcmp(fname, "hosting.config") == 0) {
lmgmt->signalFileChange("proxy.config.cache.hosting_filename");
} else if (strcmp(fname, "log_hosts.config") == 0) {
lmgmt->signalFileChange("proxy.config.log.hosts_config_file");
} else if (strcmp(fname, "logs_xml.config") == 0) {
lmgmt->signalFileChange("proxy.config.log.xml_config_file");
} else if (strcmp(fname, "splitdns.config") == 0) {
lmgmt->signalFileChange("proxy.config.dns.splitdns.filename");
} else if (strcmp(fname, "plugin.config") == 0) {
mgmt_log(stderr, "[fileUpdated] plugin.config file has been modified\n");
} else if (strcmp(fname, "ssl_multicert.config") == 0) {
lmgmt->signalFileChange("proxy.config.ssl.server.multicert.filename");
} else if (strcmp(fname, "proxy.config.body_factory.template_sets_dir") == 0) {
lmgmt->signalFileChange("proxy.config.body_factory.template_sets_dir");
} else if (strcmp(fname, "stats.config.xml") == 0) {
if (statProcessor) {
statProcessor->rereadConfig();
}
mgmt_log(stderr, "[fileUpdated] stats.config.xml file has been modified\n");
} else if (strcmp(fname, "congestion.config") == 0) {
lmgmt->signalFileChange("proxy.config.http.congestion_control.filename");
} else if (strcmp(fname, "prefetch.config") == 0) {
lmgmt->signalFileChange("proxy.config.prefetch.config_file");
} else {
mgmt_elog(stderr, 0, "[fileUpdated] Unknown config file updated '%s'\n", fname);
}
return;
} /* End fileUpdate */
#if TS_USE_POSIX_CAP
/** Restore capabilities after user id change.
This manipulates LINUX capabilities so that this process
can perform certain privileged operations even if it is
no longer running as a privilege user.
@internal
I tried using
@code
prctl(PR_SET_KEEPCAPS, 1);
@endcode
but that had no effect even though the call reported success.
Only explicit capability manipulation was effective.
It does not appear to be necessary to set the capabilities on the
executable if originally run as root. That may be needed if
started as a user without that capability.
*/
int
restoreCapabilities() {
int zret = 0; // return value.
cap_t cap_set = cap_get_proc(); // current capabilities
// Make a list of the capabilities we want turned on.
cap_value_t cap_list[] = {
CAP_NET_ADMIN, ///< Set socket transparency.
CAP_NET_BIND_SERVICE, ///< Low port (e.g. 80) binding.
CAP_IPC_LOCK ///< Lock IPC objects.
};
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
// void runAsUser(...)
//
// If we are root, switched to user to run as
// specified in records.config
//
// If we are not root, do nothing
//
void
runAsUser(char *userName)
{
uid_t uid, euid;
struct passwd *result;
const int bufSize = 1024;
char buf[bufSize];
uid = getuid();
euid = geteuid();
if (uid == 0 || euid == 0) {
/* Figure out what user we should run as */
Debug("lm", "[runAsUser] Attempting to run as user '%s'\n", userName);
if (userName == NULL || userName[0] == '\0') {
mgmt_elog(stderr, 0, "[runAsUser] Fatal Error: proxy.config.admin.user_id is not set\n");
_exit(1);
}
// this is behaving weird. refer to getpwnam(3C) sparc -jcoates
// this looks like the POSIX getpwnam_r
struct passwd passwdInfo;
struct passwd *ppasswd = NULL;
result = NULL;
int res;
if (*userName == '#') {
int uuid = atoi(userName + 1);
if (uuid == -1)
uuid = (int)uid;
res = getpwuid_r((uid_t)uuid, &passwdInfo, buf, bufSize, &ppasswd);
}
else {
res = getpwnam_r(&userName[0], &passwdInfo, buf, bufSize, &ppasswd);
}
if (!res && ppasswd) {
result = ppasswd;
}
if (result == NULL) {
mgmt_elog(stderr, 0, "[runAsUser] Fatal Error: Unable to get info about user %s : %s\n", userName, strerror(errno));
_exit(1);
}
if (setegid(result->pw_gid) != 0 || seteuid(result->pw_uid) != 0) {
mgmt_elog(stderr, 0, "[runAsUser] Fatal Error: Unable to switch to user %s : %s\n", userName, strerror(errno));
_exit(1);
}
uid = getuid();
euid = geteuid();
Debug("lm", "[runAsUser] Running with uid: '%d' euid: '%d'\n", uid, euid);
if (uid != result->pw_uid && euid != result->pw_uid) {
mgmt_elog(stderr, 0, "[runAsUser] Fatal Error: Failed to switch to user %s\n", userName);
_exit(1);
}
// setup supplementary groups if it is not set.
if (0 == getgroups(0, NULL)) {
initgroups(&userName[0],result->pw_gid);
}
#if TS_USE_POSIX_CAP
if (0 != restoreCapabilities()) {
mgmt_elog(stderr, 0, "[runAsUser] Error: Failed to restore capabilities after switch to user %s.\n", userName);
}
#endif
}
} /* End runAsUser() */
// void extractConfigInfo(...)
//
// We need to get certain records.config values while we are
// root. We can not use LMRecords to get them because the constructor
// for LMRecords creates the mgmt DBM and we do not want that to
// be owned as root. This function extracts that info from
// records.config
//
//
void
extractConfigInfo(char *mgmt_path, const char *recs_conf, char *userName, int *fds_throttle)
{
char file[1024];
bool useridFound = false;
bool throttleFound = false;
/* Figure out what user we should run as */
if (mgmt_path && recs_conf) {
FILE *fin;
snprintf(file, sizeof(file), "%s/%s.shadow", mgmt_path, recs_conf);
if (!(fin = fopen(file, "r"))) {
ink_filepath_make(file, sizeof(file), mgmt_path, recs_conf);
if (!(fin = fopen(file, "r"))) {
mgmt_elog(stderr, errno, "[extractConfigInfo] Unable to open config file(%s)\n", file);
_exit(1);
}
}
// Get 'user id' and 'network connections throttle limit'
while (((!useridFound) || (!throttleFound)) && fgets(file, 1024, fin)) {
if (strstr(file, "CONFIG proxy.config.admin.user_id STRING")) {
//coverity[secure_coding]
if ((sscanf(file, "CONFIG proxy.config.admin.user_id STRING %1023s\n", userName) == 1) &&
strcmp(userName, "NULL") != 0) {
useridFound = true;
}
} else if (strstr(file, "CONFIG proxy.config.net.connections_throttle INT")) {
if ((sscanf(file, "CONFIG proxy.config.net.connections_throttle INT %d\n", fds_throttle) == 1)) {
throttleFound = true;
}
}
}
fclose(fin);
} else {
mgmt_elog(stderr, 0, "[extractConfigInfo] Fatal Error: unable to access records file\n");
_exit(1);
}
if (useridFound == false) {
mgmt_elog(stderr, 0, "[extractConfigInfo] Fatal Error: proxy.config.admin.user_id is not set\n");
_exit(1);
}
} /* End extractConfigInfo() */