1 /** @file
2
3 A brief file description
4
5 @section license License
6
7 Licensed to the Apache Software Foundation (ASF) under one
8 or more contributor license agreements. See the NOTICE file
9 distributed with this work for additional information
10 regarding copyright ownership. The ASF licenses this file
11 to you under the Apache License, Version 2.0 (the
12 "License"); you may not use this file except in compliance
13 with the License. You may obtain a copy of the License at
14
15 http://www.apache.org/licenses/LICENSE-2.0
16
17 Unless required by applicable law or agreed to in writing, software
18 distributed under the License is distributed on an "AS IS" BASIS,
19 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 See the License for the specific language governing permissions and
21 limitations under the License.
22 */
23
24 /****************************************************************************
25
26 Main.cc
27
28 This is the primary source file for the proxy cache system.
29
30
31 ****************************************************************************/
32
33 #include "tscore/ink_platform.h"
34 #include "tscore/ink_sys_control.h"
35 #include "tscore/ink_args.h"
36 #include "tscore/ink_lockfile.h"
37 #include "tscore/ink_stack_trace.h"
38 #include "tscore/ink_syslog.h"
39 #include "tscore/hugepages.h"
40 #include "tscore/runroot.h"
41 #include "tscore/Filenames.h"
42 #include "tscore/ts_file.h"
43
44 #include "ts/ts.h" // This is sadly needed because of us using TSThreadInit() for some reason.
45
46 #include <syslog.h>
47 #include <algorithm>
48 #include <atomic>
49 #include <list>
50 #include <string>
51
52 #if !defined(linux)
53 #include <sys/lock.h>
54 #endif
55
56 #if defined(linux)
57 extern "C" int plock(int);
58 #else
59 #include <sys/filio.h>
60 #endif
61
62 #if HAVE_MCHECK_H
63 #include <mcheck.h>
64 #endif
65
66 #include "Main.h"
67 #include "tscore/signals.h"
68 #include "P_EventSystem.h"
69 #include "P_Net.h"
70 #include "P_QUICNetProcessor.h"
71 #include "P_UDPNet.h"
72 #include "P_DNS.h"
73 #include "P_SplitDNS.h"
74 #include "P_HostDB.h"
75 #include "P_Cache.h"
76 #include "tscore/I_Layout.h"
77 #include "I_Machine.h"
78 #include "RecordsConfig.h"
79 #include "records/I_RecProcess.h"
80 #include "Transform.h"
81 #include "ProcessManager.h"
82 #include "ProxyConfig.h"
83 #include "HttpProxyServerMain.h"
84 #include "HttpBodyFactory.h"
85 #include "ProxySession.h"
86 #include "logging/Log.h"
87 #include "CacheControl.h"
88 #include "IPAllow.h"
89 #include "ParentSelection.h"
90 #include "HostStatus.h"
91 #include "MgmtUtils.h"
92 #include "StatPages.h"
93 #include "HTTP.h"
94 #include "HuffmanCodec.h"
95 #include "Plugin.h"
96 #include "DiagsConfig.h"
97 #include "RemapConfig.h"
98 #include "RemapPluginInfo.h"
99 #include "RemapProcessor.h"
100 #include "I_Tasks.h"
101 #include "InkAPIInternal.h"
102 #include "HTTP2.h"
103 #include "tscore/ink_config.h"
104 #include "P_SSLSNI.h"
105 #include "P_SSLClientUtils.h"
106
107 #if TS_USE_QUIC == 1
108 #include "Http3.h"
109 #include "Http3Config.h"
110 #endif
111
112 #include "tscore/ink_cap.h"
113
114 #if TS_HAS_PROFILER
115 #include <gperftools/profiler.h>
116 #include <gperftools/heap-profiler.h>
117 #endif
118
119 //
120 // Global Data
121 //
122 #define DEFAULT_COMMAND_FLAG 0
123
124 #define DEFAULT_REMOTE_MANAGEMENT_FLAG 0
125 #define DIAGS_LOG_FILENAME "diags.log"
126
127 static const long MAX_LOGIN = ink_login_name_max();
128
129 static void mgmt_restart_shutdown_callback(ts::MemSpan<void>);
130 static void mgmt_drain_callback(ts::MemSpan<void>);
131 static void mgmt_storage_device_cmd_callback(int cmd, std::string_view const &arg);
132 static void mgmt_lifecycle_msg_callback(ts::MemSpan<void>);
133 static void init_ssl_ctx_callback(void *ctx, bool server);
134 static void load_ssl_file_callback(const char *ssl_file);
135 static void task_threads_started_callback();
136
137 // We need these two to be accessible somewhere else now
138 int num_of_net_threads = ink_number_of_processors();
139 int num_accept_threads = 0;
140
141 static int num_of_udp_threads = 0;
142 static int num_task_threads = 0;
143
144 static char *http_accept_port_descriptor;
145 int http_accept_file_descriptor = NO_FD;
146 static bool enable_core_file_p = false; // Enable core file dump?
147 int command_flag = DEFAULT_COMMAND_FLAG;
148 int command_index = -1;
149 bool command_valid = false;
150 // Commands that have special processing / requirements.
151 static const char *CMD_VERIFY_CONFIG = "verify_config";
152 #if TS_HAS_TESTS
153 static char regression_test[1024] = "";
154 static int regression_list = 0;
155 static int regression_level = REGRESSION_TEST_NONE;
156 #endif
157 int auto_clear_hostdb_flag = 0;
158 extern int fds_limit;
159
160 static char command_string[512] = "";
161 static char conf_dir[512] = "";
162 int remote_management_flag = DEFAULT_REMOTE_MANAGEMENT_FLAG;
163 static char bind_stdout[512] = "";
164 static char bind_stderr[512] = "";
165
166 static char error_tags[1024] = "";
167 static char action_tags[1024] = "";
168 static int show_statistics = 0;
169 static inkcoreapi DiagsConfig *diagsConfig = nullptr;
170 HttpBodyFactory *body_factory = nullptr;
171
172 static int accept_mss = 0;
173 static int poll_timeout = -1; // No value set.
174 static int cmd_disable_freelist = 0;
175 static bool signal_received[NSIG];
176
177 // 1: the main thread delayed accepting, start accepting.
178 // 0: delay accept, wait for cache initialization.
179 // -1: cache is already initialized, don't delay.
180 static int delay_listen_for_cache = 0;
181
182 AppVersionInfo appVersionInfo; // Build info for this application
183
184 static ArgumentDescription argument_descriptions[] = {
185 {"net_threads", 'n', "Number of Net Threads", "I", &num_of_net_threads, "PROXY_NET_THREADS", nullptr},
186 {"udp_threads", 'U', "Number of UDP Threads", "I", &num_of_udp_threads, "PROXY_UDP_THREADS", nullptr},
187 {"accept_thread", 'a', "Use an Accept Thread", "T", &num_accept_threads, "PROXY_ACCEPT_THREAD", nullptr},
188 {"accept_till_done", 'b', "Accept Till Done", "T", &accept_till_done, "PROXY_ACCEPT_TILL_DONE", nullptr},
189 {"httpport", 'p', "Port descriptor for HTTP Accept", "S*", &http_accept_port_descriptor, "PROXY_HTTP_ACCEPT_PORT", nullptr},
190 {"disable_freelist", 'f', "Disable the freelist memory allocator", "T", &cmd_disable_freelist, "PROXY_DPRINTF_LEVEL", nullptr},
191 {"disable_pfreelist", 'F', "Disable the freelist memory allocator in ProxyAllocator", "T", &cmd_disable_pfreelist,
192 "PROXY_DPRINTF_LEVEL", nullptr},
193 {"maxRecords", 'm', "Max number of librecords metrics and configurations (default & minimum: 1600)", "I", &max_records_entries,
194 "PROXY_MAX_RECORDS", nullptr},
195
196 #if TS_HAS_TESTS
197 {"regression", 'R', "Regression Level (quick:1..long:3)", "I", ®ression_level, "PROXY_REGRESSION", nullptr},
198 {"regression_test", 'r', "Run Specific Regression Test", "S512", regression_test, "PROXY_REGRESSION_TEST", nullptr},
199 {"regression_list", 'l', "List Regression Tests", "T", ®ression_list, "PROXY_REGRESSION_LIST", nullptr},
200 #endif // TS_HAS_TESTS
201
202 #if TS_USE_DIAGS
203 {"debug_tags", 'T', "Vertical-bar-separated Debug Tags", "S1023", error_tags, "PROXY_DEBUG_TAGS", nullptr},
204 {"action_tags", 'B', "Vertical-bar-separated Behavior Tags", "S1023", action_tags, "PROXY_BEHAVIOR_TAGS", nullptr},
205 #endif
206
207 {"interval", 'i', "Statistics Interval", "I", &show_statistics, "PROXY_STATS_INTERVAL", nullptr},
208 {"remote_management", 'M', "Remote Management", "T", &remote_management_flag, "PROXY_REMOTE_MANAGEMENT", nullptr},
209 {"command", 'C',
210 "Maintenance Command to Execute\n"
211 " Commands: list, check, clear, clear_cache, clear_hostdb, verify_config, verify_global_plugin, verify_remap_plugin, help",
212 "S511", &command_string, "PROXY_COMMAND_STRING", nullptr},
213 {"conf_dir", 'D', "config dir to verify", "S511", &conf_dir, "PROXY_CONFIG_CONFIG_DIR", nullptr},
214 {"clear_hostdb", 'k', "Clear HostDB on Startup", "F", &auto_clear_hostdb_flag, "PROXY_CLEAR_HOSTDB", nullptr},
215 {"clear_cache", 'K', "Clear Cache on Startup", "F", &cacheProcessor.auto_clear_flag, "PROXY_CLEAR_CACHE", nullptr},
216 {"bind_stdout", '-', "Regular file to bind stdout to", "S512", &bind_stdout, "PROXY_BIND_STDOUT", nullptr},
217 {"bind_stderr", '-', "Regular file to bind stderr to", "S512", &bind_stderr, "PROXY_BIND_STDERR", nullptr},
218 {"accept_mss", '-', "MSS for client connections", "I", &accept_mss, nullptr, nullptr},
219 {"poll_timeout", 't', "poll timeout in milliseconds", "I", &poll_timeout, nullptr, nullptr},
220 HELP_ARGUMENT_DESCRIPTION(),
221 VERSION_ARGUMENT_DESCRIPTION(),
222 RUNROOT_ARGUMENT_DESCRIPTION(),
223 };
224
225 struct AutoStopCont : public Continuation {
226 int
mainEventAutoStopCont227 mainEvent(int /* event */, Event * /* e */)
228 {
229 TSSystemState::stop_ssl_handshaking();
230
231 APIHook *hook = lifecycle_hooks->get(TS_LIFECYCLE_SHUTDOWN_HOOK);
232 while (hook) {
233 WEAK_SCOPED_MUTEX_LOCK(lock, hook->m_cont->mutex, this_ethread());
234 hook->invoke(TS_EVENT_LIFECYCLE_SHUTDOWN, nullptr);
235 hook = hook->next();
236 }
237
238 pmgmt->stop();
239 TSSystemState::shut_down_event_system();
240 delete this;
241 return EVENT_CONT;
242 }
243
AutoStopContAutoStopCont244 AutoStopCont() : Continuation(new_ProxyMutex()) { SET_HANDLER(&AutoStopCont::mainEvent); }
245 };
246
247 class SignalContinuation : public Continuation
248 {
249 public:
SignalContinuation()250 SignalContinuation() : Continuation(new_ProxyMutex())
251 {
252 end = snap = nullptr;
253 SET_HANDLER(&SignalContinuation::periodic);
254 }
255
256 int
periodic(int,Event *)257 periodic(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
258 {
259 if (signal_received[SIGUSR1]) {
260 signal_received[SIGUSR1] = false;
261
262 // TODO: TS-567 Integrate with debugging allocators "dump" features?
263 ink_freelists_dump(stderr);
264 ResourceTracker::dump(stderr);
265
266 if (!end) {
267 end = static_cast<char *>(sbrk(0));
268 }
269
270 if (!snap) {
271 snap = static_cast<char *>(sbrk(0));
272 }
273
274 char *now = static_cast<char *>(sbrk(0));
275 Note("sbrk 0x%" PRIu64 " from first %" PRIu64 " from last %" PRIu64 "\n", (uint64_t)((ptrdiff_t)now),
276 (uint64_t)((ptrdiff_t)(now - end)), (uint64_t)((ptrdiff_t)(now - snap)));
277 snap = now;
278 }
279
280 if (signal_received[SIGUSR2]) {
281 signal_received[SIGUSR2] = false;
282
283 Debug("log", "received SIGUSR2, reloading traffic.out");
284 // reload output logfile (file is usually called traffic.out)
285 diags->set_std_output(StdStream::STDOUT, bind_stdout);
286 diags->set_std_output(StdStream::STDERR, bind_stderr);
287 if (diags->reseat_diagslog()) {
288 Note("Reseated %s", DIAGS_LOG_FILENAME);
289 } else {
290 Note("Could not reseat %s", DIAGS_LOG_FILENAME);
291 }
292 // Reload any of the other moved log files (such as the ones in logging.yaml).
293 Log::handle_log_rotation_request();
294 }
295
296 if (signal_received[SIGTERM] || signal_received[SIGINT]) {
297 signal_received[SIGTERM] = false;
298 signal_received[SIGINT] = false;
299
300 RecInt timeout = 0;
301 if (RecGetRecordInt("proxy.config.stop.shutdown_timeout", &timeout) == REC_ERR_OKAY && timeout) {
302 RecSetRecordInt("proxy.node.config.draining", 1, REC_SOURCE_DEFAULT);
303 TSSystemState::drain(true);
304 if (!remote_management_flag) {
305 // Close listening sockets here only if TS is running standalone
306 RecInt close_sockets = 0;
307 if (RecGetRecordInt("proxy.config.restart.stop_listening", &close_sockets) == REC_ERR_OKAY && close_sockets) {
308 stop_HttpProxyServer();
309 }
310 }
311 }
312
313 Debug("server", "received exit signal, shutting down in %" PRId64 "secs", timeout);
314
315 // Shutdown in `timeout` seconds (or now if that is 0).
316 eventProcessor.schedule_in(new AutoStopCont(), HRTIME_SECONDS(timeout));
317 }
318
319 return EVENT_CONT;
320 }
321
322 private:
323 const char *end;
324 const char *snap;
325 };
326
327 class TrackerContinuation : public Continuation
328 {
329 public:
330 int baseline_taken;
331 int use_baseline;
332
TrackerContinuation()333 TrackerContinuation() : Continuation(new_ProxyMutex())
334 {
335 SET_HANDLER(&TrackerContinuation::periodic);
336 use_baseline = 0;
337 // TODO: ATS prefix all those environment stuff or
338 // even better use config since env can be
339 // different for parent and child process users.
340 //
341 if (getenv("MEMTRACK_BASELINE")) {
342 use_baseline = 1;
343 }
344
345 baseline_taken = 0;
346 }
347
~TrackerContinuation()348 ~TrackerContinuation() override { mutex = nullptr; }
349 int
periodic(int event,Event *)350 periodic(int event, Event * /* e ATS_UNUSED */)
351 {
352 if (event == EVENT_IMMEDIATE) {
353 // rescheduled from periodic to immediate event
354 // this is the indication to terminate this tracker.
355 delete this;
356 return EVENT_DONE;
357 }
358 if (use_baseline) {
359 // TODO: TS-567 Integrate with debugging allocators "dump" features?
360 ink_freelists_dump_baselinerel(stderr);
361 } else {
362 // TODO: TS-567 Integrate with debugging allocators "dump" features?
363 ink_freelists_dump(stderr);
364 ResourceTracker::dump(stderr);
365 }
366 if (!baseline_taken && use_baseline) {
367 ink_freelists_snap_baseline();
368 // TODO: TS-567 Integrate with debugging allocators "dump" features?
369 baseline_taken = 1;
370 }
371 return EVENT_CONT;
372 }
373 };
374
375 // This continuation is used to periodically check on diags.log, and rotate
376 // the logs if necessary
377 class DiagsLogContinuation : public Continuation
378 {
379 public:
DiagsLogContinuation()380 DiagsLogContinuation() : Continuation(new_ProxyMutex()) { SET_HANDLER(&DiagsLogContinuation::periodic); }
381 int
periodic(int,Event *)382 periodic(int /* event ATS_UNUSED */, Event * /* e ATS_UNUSED */)
383 {
384 Debug("log", "in DiagsLogContinuation, checking on diags.log");
385
386 // First, let us update the rolling config values for diagslog. We
387 // do not need to update the config values for outputlog because
388 // traffic_server never actually rotates outputlog. outputlog is always
389 // rotated in traffic_manager. The reason being is that it is difficult
390 // to send a notification from TS to TM, informing TM that outputlog has
391 // been rolled. It is much easier sending a notification (in the form
392 // of SIGUSR2) from TM -> TS.
393 int diags_log_roll_int = (int)REC_ConfigReadInteger("proxy.config.diags.logfile.rolling_interval_sec");
394 int diags_log_roll_size = (int)REC_ConfigReadInteger("proxy.config.diags.logfile.rolling_size_mb");
395 int diags_log_roll_enable = (int)REC_ConfigReadInteger("proxy.config.diags.logfile.rolling_enabled");
396 diags->config_roll_diagslog((RollingEnabledValues)diags_log_roll_enable, diags_log_roll_int, diags_log_roll_size);
397
398 if (diags->should_roll_diagslog()) {
399 Note("Rolled %s", DIAGS_LOG_FILENAME);
400 }
401 return EVENT_CONT;
402 }
403 };
404
405 class MemoryLimit : public Continuation
406 {
407 public:
MemoryLimit()408 MemoryLimit() : Continuation(new_ProxyMutex())
409 {
410 memset(&_usage, 0, sizeof(_usage));
411 SET_HANDLER(&MemoryLimit::periodic);
412 RecRegisterStatInt(RECT_PROCESS, "proxy.process.traffic_server.memory.rss", static_cast<RecInt>(0), RECP_NON_PERSISTENT);
413 }
414
~MemoryLimit()415 ~MemoryLimit() override { mutex = nullptr; }
416
417 int
periodic(int event,Event * e)418 periodic(int event, Event *e)
419 {
420 if (event == EVENT_IMMEDIATE) {
421 // rescheduled from periodic to immediate event
422 // this is the indication to terminate
423 delete this;
424 return EVENT_DONE;
425 }
426
427 // "reload" the setting, we don't do this often so not expensive
428 _memory_limit = REC_ConfigReadInteger("proxy.config.memory.max_usage");
429 _memory_limit = _memory_limit >> 10; // divide by 1024
430
431 if (getrusage(RUSAGE_SELF, &_usage) == 0) {
432 RecSetRecordInt("proxy.process.traffic_server.memory.rss", _usage.ru_maxrss << 10, REC_SOURCE_DEFAULT); // * 1024
433 Debug("server", "memory usage - ru_maxrss: %ld memory limit: %" PRId64, _usage.ru_maxrss, _memory_limit);
434 if (_memory_limit > 0) {
435 if (_usage.ru_maxrss > _memory_limit) {
436 if (net_memory_throttle == false) {
437 net_memory_throttle = true;
438 Debug("server", "memory usage exceeded limit - ru_maxrss: %ld memory limit: %" PRId64, _usage.ru_maxrss, _memory_limit);
439 }
440 } else {
441 if (net_memory_throttle == true) {
442 net_memory_throttle = false;
443 Debug("server", "memory usage under limit - ru_maxrss: %ld memory limit: %" PRId64, _usage.ru_maxrss, _memory_limit);
444 }
445 }
446 } else {
447 // this feature has not been enabled
448 Debug("server", "limiting connections based on memory usage has been disabled");
449 e->cancel();
450 delete this;
451 return EVENT_DONE;
452 }
453 }
454 return EVENT_CONT;
455 }
456
457 private:
458 int64_t _memory_limit = 0;
459 struct rusage _usage;
460 };
461
462 /** Gate the emission of the "Traffic Server is fuly initialized" log message.
463 *
464 * This message is intended to be helpful to users who want to know that
465 * Traffic Server is not just running but has become fully initialized and is
466 * ready to optimize traffic. This is in contrast to the "traffic server is
467 * running" message which can be printed before either of these conditions.
468 *
469 * This function is called on each initialization state transition. Currently,
470 * the two state transitions of interest are:
471 *
472 * 1. The cache is initialized.
473 * 2. The ports are open and accept has been called upon them.
474 *
475 * Note that Traffic Server configures the port objects and may even open the
476 * ports before calling accept on those ports. The difference between these two
477 * events is communicated to plugins via the
478 * TS_LIFECYCLE_PORTS_INITIALIZED_HOOK and TS_LIFECYCLE_PORTS_READY_HOOK hooks.
479 * If wait_for_cache is enabled, the difference in time between these events
480 * may measure in the tens of milliseconds. The message emitted by this
481 * function happens after this full lifecycle takes place on these ports and
482 * after cache is initialized.
483 */
484 static void
emit_fully_initialized_message()485 emit_fully_initialized_message()
486 {
487 static std::atomic<unsigned int> initialization_state_counter = 0;
488
489 // See the doxygen comment above explaining what the states are that
490 // constitute Traffic Server being fully initialized.
491 constexpr unsigned int num_initialization_states = 2;
492
493 if (++initialization_state_counter == num_initialization_states) {
494 Note("Traffic Server is fully initialized.");
495 }
496 }
497
498 void
set_debug_ip(const char * ip_string)499 set_debug_ip(const char *ip_string)
500 {
501 if (ip_string) {
502 diags->debug_client_ip.load(ip_string);
503 } else {
504 diags->debug_client_ip.invalidate();
505 }
506 }
507
508 static int
update_debug_client_ip(const char *,RecDataT,RecData data,void *)509 update_debug_client_ip(const char * /*name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData data,
510 void * /* data_type ATS_UNUSED */)
511 {
512 set_debug_ip(data.rec_string);
513 return 0;
514 }
515
516 static int
init_memory_tracker(const char * config_var,RecDataT,RecData data,void *)517 init_memory_tracker(const char *config_var, RecDataT /* type ATS_UNUSED */, RecData data, void * /* cookie ATS_UNUSED */)
518 {
519 static Event *tracker_event = nullptr;
520 Event *preE;
521 int dump_mem_info_frequency = 0;
522
523 // set tracker_event to NULL, and return previous value
524 preE = ink_atomic_swap(&tracker_event, static_cast<Event *>(nullptr));
525
526 if (config_var) {
527 dump_mem_info_frequency = data.rec_int;
528 } else {
529 dump_mem_info_frequency = REC_ConfigReadInteger("proxy.config.dump_mem_info_frequency");
530 }
531
532 Debug("tracker", "init_memory_tracker called [%d]", dump_mem_info_frequency);
533
534 if (preE) {
535 eventProcessor.schedule_imm(preE->continuation, ET_CALL);
536 preE->cancel();
537 }
538
539 if (dump_mem_info_frequency > 0) {
540 tracker_event = eventProcessor.schedule_every(new TrackerContinuation, HRTIME_SECONDS(dump_mem_info_frequency), ET_CALL);
541 }
542
543 return 1;
544 }
545
546 static void
proxy_signal_handler(int signo,siginfo_t * info,void * ctx)547 proxy_signal_handler(int signo, siginfo_t *info, void *ctx)
548 {
549 if ((unsigned)signo < countof(signal_received)) {
550 signal_received[signo] = true;
551 }
552
553 // These signals are all handled by SignalContinuation.
554 switch (signo) {
555 case SIGHUP:
556 case SIGINT:
557 case SIGTERM:
558 case SIGUSR1:
559 case SIGUSR2:
560 return;
561 }
562
563 signal_format_siginfo(signo, info, appVersionInfo.AppStr);
564
565 #if TS_HAS_PROFILER
566 HeapProfilerDump("/tmp/ts_end.hprof");
567 HeapProfilerStop();
568 ProfilerStop();
569 #endif
570
571 // We don't expect any crashing signals here because, but
572 // forward to the default handler just to be robust.
573 if (signal_is_crash(signo)) {
574 signal_crash_handler(signo, info, ctx);
575 }
576 }
577
578 //
579 // Initialize operating system related information/services
580 //
581 static void
init_system()582 init_system()
583 {
584 signal_register_default_handler(proxy_signal_handler);
585 signal_register_crash_handler(signal_crash_handler);
586
587 syslog(LOG_NOTICE, "NOTE: --- %s Starting ---", appVersionInfo.AppStr);
588 syslog(LOG_NOTICE, "NOTE: %s Version: %s", appVersionInfo.AppStr, appVersionInfo.FullVersionInfoStr);
589
590 //
591 // Delimit file Descriptors
592 //
593 fds_limit = ink_max_out_rlimit(RLIMIT_NOFILE);
594 }
595
596 static void
check_lockfile()597 check_lockfile()
598 {
599 std::string rundir(RecConfigReadRuntimeDir());
600 std::string lockfile;
601 pid_t holding_pid;
602 int err;
603
604 lockfile = Layout::relative_to(rundir, SERVER_LOCK);
605
606 Lockfile server_lockfile(lockfile.c_str());
607 err = server_lockfile.Get(&holding_pid);
608
609 if (err != 1) {
610 char *reason = strerror(-err);
611 fprintf(stderr, "WARNING: Can't acquire lockfile '%s'", lockfile.c_str());
612
613 if ((err == 0) && (holding_pid != -1)) {
614 fprintf(stderr, " (Lock file held by process ID %ld)\n", static_cast<long>(holding_pid));
615 } else if ((err == 0) && (holding_pid == -1)) {
616 fprintf(stderr, " (Lock file exists, but can't read process ID)\n");
617 } else if (reason) {
618 fprintf(stderr, " (%s)\n", reason);
619 } else {
620 fprintf(stderr, "\n");
621 }
622 ::exit(1);
623 }
624 }
625
626 static void
check_config_directories()627 check_config_directories()
628 {
629 std::string rundir(RecConfigReadRuntimeDir());
630 std::string sysconfdir(RecConfigReadConfigDir());
631
632 if (access(sysconfdir.c_str(), R_OK) == -1) {
633 fprintf(stderr, "unable to access() config dir '%s': %d, %s\n", sysconfdir.c_str(), errno, strerror(errno));
634 fprintf(stderr, "please set the 'TS_ROOT' environment variable\n");
635 ::exit(1);
636 }
637
638 if (access(rundir.c_str(), R_OK | W_OK) == -1) {
639 fprintf(stderr, "unable to access() local state dir '%s': %d, %s\n", rundir.c_str(), errno, strerror(errno));
640 fprintf(stderr, "please set 'proxy.config.local_state_dir'\n");
641 ::exit(1);
642 }
643 }
644
645 //
646 // Startup process manager
647 //
648 static void
initialize_process_manager()649 initialize_process_manager()
650 {
651 mgmt_use_syslog();
652
653 // Temporary Hack to Enable Communication with LocalManager
654 if (getenv("PROXY_REMOTE_MGMT")) {
655 remote_management_flag = true;
656 }
657
658 if (remote_management_flag) {
659 // We are being managed by traffic_manager, TERM ourselves if it goes away.
660 EnableDeathSignal(SIGTERM);
661 }
662
663 RecProcessInit(remote_management_flag ? RECM_CLIENT : RECM_STAND_ALONE, diags);
664 LibRecordsConfigInit();
665
666 // Start up manager
667 pmgmt = new ProcessManager(remote_management_flag);
668
669 // Lifecycle callbacks can potentially be invoked from this thread, so force thread initialization
670 // to make the TS API work.
671 pmgmt->start(TSThreadInit, TSThreadDestroy);
672
673 RecProcessInitMessage(remote_management_flag ? RECM_CLIENT : RECM_STAND_ALONE);
674 pmgmt->reconfigure();
675 check_config_directories();
676
677 //
678 // Define version info records
679 //
680 RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.short", appVersionInfo.VersionStr, RECP_NON_PERSISTENT);
681 RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.long", appVersionInfo.FullVersionInfoStr, RECP_NON_PERSISTENT);
682 RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_number", appVersionInfo.BldNumStr, RECP_NON_PERSISTENT);
683 RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_time", appVersionInfo.BldTimeStr, RECP_NON_PERSISTENT);
684 RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_date", appVersionInfo.BldDateStr, RECP_NON_PERSISTENT);
685 RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_machine", appVersionInfo.BldMachineStr,
686 RECP_NON_PERSISTENT);
687 RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.build_person", appVersionInfo.BldPersonStr,
688 RECP_NON_PERSISTENT);
689 }
690
691 #define CMD_ERROR -2 // serious error, exit maintenance mode
692 #define CMD_FAILED -1 // error, but recoverable
693 #define CMD_OK 0 // ok, or minor (user) error
694 #define CMD_HELP 1 // ok, print help
695 #define CMD_IN_PROGRESS 2 // task not completed. don't exit
696
697 static int
cmd_list(char *)698 cmd_list(char * /* cmd ATS_UNUSED */)
699 {
700 printf("LIST\n\n");
701
702 // show hostdb size
703
704 int h_size = 120000;
705 REC_ReadConfigInteger(h_size, "proxy.config.hostdb.size");
706 printf("Host Database size:\t%d\n", h_size);
707
708 // show cache config information....
709
710 Note("Cache Storage:");
711 Store tStore;
712 Result result = tStore.read_config();
713
714 if (result.failed()) {
715 Note("Failed to read cache storage configuration: %s", result.message());
716 return CMD_FAILED;
717 } else {
718 tStore.write_config_data(fileno(stdout));
719 return CMD_OK;
720 }
721 }
722
723 /** Parse the given string and skip the first word.
724 *
725 * Words are assumed to be separated by spaces or tabs.
726 *
727 * @param[in] cmd The string whose first word will be skipped.
728 *
729 * @return The pointer in the string cmd to the second word in the string, or
730 * nullptr if there is no second word.
731 */
732 static char *
skip(char * cmd)733 skip(char *cmd)
734 {
735 // Skip initial white space.
736 cmd += strspn(cmd, " \t");
737 // Point to the beginning of the next white space.
738 cmd = strpbrk(cmd, " \t");
739 if (!cmd) {
740 return cmd;
741 }
742 // Skip the second white space so that cmd now points to the beginning of the
743 // second word.
744 cmd += strspn(cmd, " \t");
745 return cmd;
746 }
747
748 // Handler for things that need to wait until the cache is initialized.
749 static void
CB_After_Cache_Init()750 CB_After_Cache_Init()
751 {
752 APIHook *hook;
753 int start;
754
755 start = ink_atomic_swap(&delay_listen_for_cache, -1);
756 emit_fully_initialized_message();
757
758 #if TS_ENABLE_FIPS == 0
759 // Check for cache BC after the cache is initialized and before listen, if possible.
760 if (cacheProcessor.min_stripe_version._major < CACHE_DB_MAJOR_VERSION) {
761 // Versions before 23 need the MMH hash.
762 if (cacheProcessor.min_stripe_version._major < 23) {
763 Debug("cache_bc", "Pre 4.0 stripe (cache version %d.%d) found, forcing MMH hash for cache URLs",
764 cacheProcessor.min_stripe_version._major, cacheProcessor.min_stripe_version._minor);
765 URLHashContext::Setting = URLHashContext::MMH;
766 }
767 }
768 #endif
769
770 if (1 == start) {
771 // The delay_listen_for_cache value was 1, therefore the main function
772 // delayed the call to start_HttpProxyServer until we got here. We must
773 // call accept on the ports now that the cache is initialized.
774 Debug("http_listen", "Delayed listen enable, cache initialization finished");
775 start_HttpProxyServer();
776 emit_fully_initialized_message();
777 }
778
779 time_t cache_ready_at = time(nullptr);
780 RecSetRecordInt("proxy.node.restarts.proxy.cache_ready_time", cache_ready_at, REC_SOURCE_DEFAULT);
781
782 // Alert the plugins the cache is initialized.
783 hook = lifecycle_hooks->get(TS_LIFECYCLE_CACHE_READY_HOOK);
784 while (hook) {
785 hook->invoke(TS_EVENT_LIFECYCLE_CACHE_READY, nullptr);
786 hook = hook->next();
787 }
788 }
789
790 void
CB_cmd_cache_clear()791 CB_cmd_cache_clear()
792 {
793 if (cacheProcessor.IsCacheEnabled() == CACHE_INITIALIZED) {
794 Note("CLEAR, succeeded");
795 ::exit(0);
796 } else if (cacheProcessor.IsCacheEnabled() == CACHE_INIT_FAILED) {
797 Note("unable to open Cache, CLEAR failed");
798 ::exit(1);
799 }
800 }
801
802 void
CB_cmd_cache_check()803 CB_cmd_cache_check()
804 {
805 int res = 0;
806 if (cacheProcessor.IsCacheEnabled() == CACHE_INITIALIZED) {
807 res = cacheProcessor.dir_check(false) < 0 || res;
808 cacheProcessor.stop();
809 const char *n = "CHECK";
810
811 if (res) {
812 printf("\n%s failed", n);
813 ::exit(1);
814 } else {
815 printf("\n%s succeeded\n", n);
816 ::exit(0);
817 }
818 } else if (cacheProcessor.IsCacheEnabled() == CACHE_INIT_FAILED) {
819 Note("unable to open Cache, Check failed");
820 ::exit(1);
821 }
822 }
823
824 static int
cmd_check_internal(char *,bool fix=false)825 cmd_check_internal(char * /* cmd ATS_UNUSED */, bool fix = false)
826 {
827 const char *n = fix ? "REPAIR" : "CHECK";
828
829 printf("%s\n\n", n);
830
831 cacheProcessor.afterInitCallbackSet(&CB_cmd_cache_check);
832 if (cacheProcessor.start_internal(PROCESSOR_CHECK) < 0) {
833 printf("\nbad cache configuration, %s failed\n", n);
834 return CMD_FAILED;
835 }
836 return CMD_IN_PROGRESS;
837 }
838
839 static int
cmd_check(char * cmd)840 cmd_check(char *cmd)
841 {
842 return cmd_check_internal(cmd, false);
843 }
844
845 #ifdef UNUSED_FUNCTION
846 static int
cmd_repair(char * cmd)847 cmd_repair(char *cmd)
848 {
849 return cmd_check_internal(cmd, true);
850 }
851 #endif
852
853 static int
cmd_clear(char * cmd)854 cmd_clear(char *cmd)
855 {
856 Note("CLEAR");
857
858 bool c_all = !strcmp(cmd, "clear");
859 bool c_hdb = !strcmp(cmd, "clear_hostdb");
860 bool c_cache = !strcmp(cmd, "clear_cache");
861
862 if (c_all || c_hdb) {
863 std::string rundir(RecConfigReadRuntimeDir());
864 std::string config(Layout::relative_to(rundir, "hostdb.config"));
865
866 Note("Clearing HostDB Configuration");
867 if (unlink(config.c_str()) < 0) {
868 Note("unable to unlink %s", config.c_str());
869 }
870 }
871
872 if (c_hdb || c_all) {
873 Note("Clearing Host Database");
874 if (hostDBProcessor.cache()->start(PROCESSOR_RECONFIGURE) < 0) {
875 Note("unable to open Host Database, CLEAR failed");
876 return CMD_FAILED;
877 }
878 hostDBProcessor.cache()->refcountcache->clear();
879 if (c_hdb) {
880 return CMD_OK;
881 }
882 }
883
884 if (c_all || c_cache) {
885 Note("Clearing Cache");
886
887 cacheProcessor.afterInitCallbackSet(&CB_cmd_cache_clear);
888 if (cacheProcessor.start_internal(PROCESSOR_RECONFIGURE) < 0) {
889 Note("unable to open Cache, CLEAR failed");
890 return CMD_FAILED;
891 }
892 return CMD_IN_PROGRESS;
893 }
894
895 return CMD_OK;
896 }
897
898 static int
cmd_verify(char *)899 cmd_verify(char * /* cmd ATS_UNUSED */)
900 {
901 unsigned char exitStatus = 0; // exit status is 8 bits
902
903 fprintf(stderr, "NOTE: VERIFY\n\n");
904
905 // initialize logging since a plugin
906 // might call TS_ERROR which needs
907 // log_rsb to be init'ed
908 Log::init(DEFAULT_REMOTE_MANAGEMENT_FLAG);
909
910 if (*conf_dir) {
911 fprintf(stderr, "NOTE: VERIFY config dir: %s...\n\n", conf_dir);
912 Layout::get()->update_sysconfdir(conf_dir);
913 }
914
915 if (!urlRewriteVerify()) {
916 exitStatus |= (1 << 0);
917 fprintf(stderr, "ERROR: Failed to load %s, exitStatus %d\n\n", ts::filename::REMAP, exitStatus);
918 } else {
919 fprintf(stderr, "INFO: Successfully loaded %s\n\n", ts::filename::REMAP);
920 }
921
922 if (RecReadConfigFile() != REC_ERR_OKAY) {
923 exitStatus |= (1 << 1);
924 fprintf(stderr, "ERROR: Failed to load %s, exitStatus %d\n\n", ts::filename::RECORDS, exitStatus);
925 } else {
926 fprintf(stderr, "INFO: Successfully loaded %s\n\n", ts::filename::RECORDS);
927 }
928
929 if (!plugin_init(true)) {
930 exitStatus |= (1 << 2);
931 fprintf(stderr, "ERROR: Failed to load %s, exitStatus %d\n\n", ts::filename::PLUGIN, exitStatus);
932 } else {
933 fprintf(stderr, "INFO: Successfully loaded %s\n\n", ts::filename::PLUGIN);
934 }
935
936 SSLInitializeLibrary();
937 SSLConfig::startup();
938 if (!SSLCertificateConfig::startup()) {
939 exitStatus |= (1 << 3);
940 fprintf(stderr, "ERROR: Failed to load ssl multicert.config, exitStatus %d\n\n", exitStatus);
941 } else {
942 fprintf(stderr, "INFO: Successfully loaded ssl multicert.config\n\n");
943 }
944
945 SSLConfig::scoped_config params;
946 if (!SSLInitClientContext(params)) {
947 exitStatus |= (1 << 4);
948 fprintf(stderr, "Can't initialize the SSL client, HTTPS in remap rules will not function %d\n\n", exitStatus);
949 } else {
950 fprintf(stderr, "INFO: Successfully initialized SSL client context\n\n");
951 }
952
953 // TODO: Add more config validation..
954
955 ::exit(exitStatus);
956
957 return 0;
958 }
959
960 enum class plugin_type_t {
961 GLOBAL,
962 REMAP,
963 };
964
965 /** Attempt to load a plugin shared object file.
966 *
967 * @param[in] plugin_type The type of plugin for which to create a PluginInfo.
968 * @param[in] plugin_path The path to the plugin's shared object file.
969 * @param[out] error Some description of why the plugin failed to load if
970 * loading it fails.
971 *
972 * @return True if the plugin loaded successfully, false otherwise.
973 */
974 static bool
load_plugin(plugin_type_t plugin_type,const fs::path & plugin_path,std::string & error)975 load_plugin(plugin_type_t plugin_type, const fs::path &plugin_path, std::string &error)
976 {
977 switch (plugin_type) {
978 case plugin_type_t::GLOBAL: {
979 void *handle, *initptr;
980 return plugin_dso_load(plugin_path.c_str(), handle, initptr, error);
981 }
982 case plugin_type_t::REMAP: {
983 auto temporary_directory = fs::temp_directory_path();
984 temporary_directory /= fs::path(std::string("verify_plugin_") + std::to_string(getpid()));
985 std::error_code ec;
986 if (!fs::create_directories(temporary_directory, ec)) {
987 std::ostringstream error_os;
988 error_os << "Could not create temporary directory " << temporary_directory.string() << ": " << ec.message();
989 error = error_os.str();
990 return false;
991 }
992 const auto runtime_path = temporary_directory / ts::file::filename(plugin_path);
993 const fs::path unused_config;
994 auto plugin_info = std::make_unique<RemapPluginInfo>(unused_config, plugin_path, runtime_path);
995 bool loaded = plugin_info->load(error);
996 if (!fs::remove(temporary_directory, ec)) {
997 fprintf(stderr, "ERROR: could not remove temporary directory '%s': %s\n", temporary_directory.c_str(), ec.message().c_str());
998 }
999 return loaded;
1000 }
1001 }
1002 // Unreached.
1003 return false;
1004 }
1005
1006 /** A helper for the verify plugin command functions.
1007 *
1008 * @param[in] args The arguments passed to the -C command option. This includes
1009 * verify_global_plugin.
1010 *
1011 * @param[in] symbols The expected symbols to verify exist in the plugin file.
1012 *
1013 * @return a CMD status code. See the CMD_ defines above in this file.
1014 */
1015 static int
verify_plugin_helper(char * args,plugin_type_t plugin_type)1016 verify_plugin_helper(char *args, plugin_type_t plugin_type)
1017 {
1018 const auto *plugin_filename = skip(args);
1019 if (!plugin_filename) {
1020 fprintf(stderr, "ERROR: verifying a plugin requires a plugin SO file path argument\n");
1021 return CMD_FAILED;
1022 }
1023
1024 fs::path plugin_path(plugin_filename);
1025 fprintf(stderr, "NOTE: verifying plugin '%s'...\n", plugin_filename);
1026
1027 if (!fs::exists(plugin_path)) {
1028 fprintf(stderr, "ERROR: verifying plugin '%s' Fail: No such file or directory\n", plugin_filename);
1029 return CMD_FAILED;
1030 }
1031
1032 auto ret = CMD_OK;
1033 std::string error;
1034 if (load_plugin(plugin_type, plugin_path, error)) {
1035 fprintf(stderr, "NOTE: verifying plugin '%s' Success\n", plugin_filename);
1036 } else {
1037 fprintf(stderr, "ERROR: verifying plugin '%s' Fail: %s\n", plugin_filename, error.c_str());
1038 ret = CMD_FAILED;
1039 }
1040 return ret;
1041 }
1042
1043 /** Verify whether a given SO file looks like a valid global plugin.
1044 *
1045 * @param[in] args The arguments passed to the -C command option. This includes
1046 * verify_global_plugin.
1047 *
1048 * @return a CMD status code. See the CMD_ defines above in this file.
1049 */
1050 static int
cmd_verify_global_plugin(char * args)1051 cmd_verify_global_plugin(char *args)
1052 {
1053 return verify_plugin_helper(args, plugin_type_t::GLOBAL);
1054 }
1055
1056 /** Verify whether a given SO file looks like a valid remap plugin.
1057 *
1058 * @param[in] args The arguments passed to the -C command option. This includes
1059 * verify_global_plugin.
1060 *
1061 * @return a CMD status code. See the CMD_ defines above in this file.
1062 */
1063 static int
cmd_verify_remap_plugin(char * args)1064 cmd_verify_remap_plugin(char *args)
1065 {
1066 return verify_plugin_helper(args, plugin_type_t::REMAP);
1067 }
1068
1069 static int cmd_help(char *cmd);
1070
1071 static const struct CMD {
1072 const char *n; // name
1073 const char *d; // description (part of a line)
1074 const char *h; // help string (multi-line)
1075 int (*f)(char *);
1076 bool no_process_lock; /// If set this command doesn't need a process level lock.
1077 } commands[] = {
1078 {"list", "List cache configuration",
1079 "LIST\n"
1080 "\n"
1081 "FORMAT: list\n"
1082 "\n"
1083 "List the sizes of the Host Database and Cache Index,\n"
1084 "and the storage available to the cache.\n",
1085 cmd_list, false},
1086 {"check", "Check the cache (do not make any changes)",
1087 "CHECK\n"
1088 "\n"
1089 "FORMAT: check\n"
1090 "\n"
1091 "Check the cache for inconsistencies or corruption.\n"
1092 "CHECK does not make any changes to the data stored in\n"
1093 "the cache. CHECK requires a scan of the contents of the\n"
1094 "cache and may take a long time for large caches.\n",
1095 cmd_check, true},
1096 {"clear", "Clear the entire cache",
1097 "CLEAR\n"
1098 "\n"
1099 "FORMAT: clear\n"
1100 "\n"
1101 "Clear the entire cache. All data in the cache is\n"
1102 "lost and the cache is reconfigured based on the current\n"
1103 "description of database sizes and available storage.\n",
1104 cmd_clear, false},
1105 {"clear_cache", "Clear the document cache",
1106 "CLEAR_CACHE\n"
1107 "\n"
1108 "FORMAT: clear_cache\n"
1109 "\n"
1110 "Clear the document cache. All documents in the cache are\n"
1111 "lost and the cache is reconfigured based on the current\n"
1112 "description of database sizes and available storage.\n",
1113 cmd_clear, false},
1114 {"clear_hostdb", "Clear the hostdb cache",
1115 "CLEAR_HOSTDB\n"
1116 "\n"
1117 "FORMAT: clear_hostdb\n"
1118 "\n"
1119 "Clear the entire hostdb cache. All host name resolution\n"
1120 "information is lost.\n",
1121 cmd_clear, false},
1122 {CMD_VERIFY_CONFIG, "Verify the config",
1123 "\n"
1124 "\n"
1125 "FORMAT: verify_config\n"
1126 "\n"
1127 "Load the config and verify traffic_server comes up correctly. \n",
1128 cmd_verify, true},
1129 {"verify_global_plugin", "Verify a global plugin's shared object file",
1130 "VERIFY_GLOBAL_PLUGIN\n"
1131 "\n"
1132 "FORMAT: verify_global_plugin [global_plugin_so_file]\n"
1133 "\n"
1134 "Load a global plugin's shared object file and verify it meets\n"
1135 "minimal plugin API requirements. \n",
1136 cmd_verify_global_plugin, false},
1137 {"verify_remap_plugin", "Verify a remap plugin's shared object file",
1138 "VERIFY_REMAP_PLUGIN\n"
1139 "\n"
1140 "FORMAT: verify_remap_plugin [remap_plugin_so_file]\n"
1141 "\n"
1142 "Load a remap plugin's shared object file and verify it meets\n"
1143 "minimal plugin API requirements. \n",
1144 cmd_verify_remap_plugin, false},
1145 {"help", "Obtain a short description of a command (e.g. 'help clear')",
1146 "HELP\n"
1147 "\n"
1148 "FORMAT: help [command_name]\n"
1149 "\n"
1150 "EXAMPLES: help help\n"
1151 " help commit\n"
1152 "\n"
1153 "Provide a short description of a command (like this).\n",
1154 cmd_help, false},
1155 };
1156
1157 static int
find_cmd_index(const char * p)1158 find_cmd_index(const char *p)
1159 {
1160 p += strspn(p, " \t");
1161 for (unsigned c = 0; c < countof(commands); c++) {
1162 const char *l = commands[c].n;
1163 while (l) {
1164 const char *s = strchr(l, '/');
1165 const char *e = strpbrk(p, " \t\n");
1166 int len = s ? s - l : strlen(l);
1167 int lenp = e ? e - p : strlen(p);
1168 if ((len == lenp) && !strncasecmp(p, l, len)) {
1169 return c;
1170 }
1171 l = s ? s + 1 : nullptr;
1172 }
1173 }
1174 return -1;
1175 }
1176
1177 /** Print the maintenance command help output.
1178 */
1179 static void
print_cmd_help()1180 print_cmd_help()
1181 {
1182 for (unsigned i = 0; i < countof(commands); i++) {
1183 printf("%25s %s\n", commands[i].n, commands[i].d);
1184 }
1185 }
1186
1187 static int
cmd_help(char * cmd)1188 cmd_help(char *cmd)
1189 {
1190 (void)cmd;
1191 printf("HELP\n\n");
1192 cmd = skip(cmd);
1193 if (!cmd) {
1194 print_cmd_help();
1195 } else {
1196 int i;
1197 if ((i = find_cmd_index(cmd)) < 0) {
1198 printf("\nno help found for: %s\n", cmd);
1199 return CMD_FAILED;
1200 }
1201 printf("Help for: %s\n\n", commands[i].n);
1202 printf("%s", commands[i].h);
1203 }
1204 return CMD_OK;
1205 }
1206
1207 static void
check_fd_limit()1208 check_fd_limit()
1209 {
1210 int fds_throttle = -1;
1211 REC_ReadConfigInteger(fds_throttle, "proxy.config.net.connections_throttle");
1212 if (fds_throttle > fds_limit - THROTTLE_FD_HEADROOM) {
1213 int new_fds_throttle = fds_limit - THROTTLE_FD_HEADROOM;
1214 if (new_fds_throttle < 1) {
1215 ink_abort("too few file descriptors (%d) available", fds_limit);
1216 }
1217 char msg[256];
1218 snprintf(msg, sizeof(msg),
1219 "connection throttle too high, "
1220 "%d (throttle) + %d (internal use) > %d (file descriptor limit), "
1221 "using throttle of %d",
1222 fds_throttle, THROTTLE_FD_HEADROOM, fds_limit, new_fds_throttle);
1223 SignalWarning(MGMT_SIGNAL_SYSTEM_ERROR, msg);
1224 }
1225 }
1226
1227 //
1228 // Command mode
1229 //
1230 static int
cmd_mode()1231 cmd_mode()
1232 {
1233 if (command_index >= 0) {
1234 return commands[command_index].f(command_string);
1235 } else if (*command_string) {
1236 Warning("unrecognized command: '%s'", command_string);
1237 printf("\n");
1238 printf("WARNING: Unrecognized command: '%s'\n", command_string);
1239 printf("\n");
1240 print_cmd_help();
1241 return CMD_FAILED; // in error
1242 } else {
1243 printf("\n");
1244 printf("WARNING\n");
1245 printf("\n");
1246 printf("The interactive command mode no longer exists.\n");
1247 printf("Use '-C <command>' to execute a command from the shell prompt.\n");
1248 printf("For example: 'traffic_server -C clear' will clear the cache.\n");
1249 return 1;
1250 }
1251 }
1252
1253 #ifdef UNUSED_FUNCTION
1254 static void
check_for_root_uid()1255 check_for_root_uid()
1256 {
1257 if ((getuid() == 0) || (geteuid() == 0)) {
1258 ProcessFatal("Traffic Server must not be run as root");
1259 }
1260 }
1261 #endif
1262
1263 static int
set_core_size(const char *,RecDataT,RecData data,void *)1264 set_core_size(const char * /* name ATS_UNUSED */, RecDataT /* data_type ATS_UNUSED */, RecData data,
1265 void * /* opaque_token ATS_UNUSED */)
1266 {
1267 RecInt size = data.rec_int;
1268 struct rlimit lim;
1269 bool failed = false;
1270
1271 if (getrlimit(RLIMIT_CORE, &lim) < 0) {
1272 failed = true;
1273 } else {
1274 if (size < 0) {
1275 lim.rlim_cur = lim.rlim_max;
1276 } else {
1277 lim.rlim_cur = (rlim_t)size;
1278 }
1279 if (setrlimit(RLIMIT_CORE, &lim) < 0) {
1280 failed = true;
1281 }
1282 enable_core_file_p = size != 0;
1283 EnableCoreFile(enable_core_file_p);
1284 }
1285
1286 if (failed == true) {
1287 Warning("Failed to set Core Limit : %s", strerror(errno));
1288 }
1289 return 0;
1290 }
1291
1292 static void
init_core_size()1293 init_core_size()
1294 {
1295 bool found;
1296 RecInt coreSize;
1297 found = (RecGetRecordInt("proxy.config.core_limit", &coreSize) == REC_ERR_OKAY);
1298
1299 if (found == false) {
1300 Warning("Unable to determine core limit");
1301 } else {
1302 RecData rec_temp;
1303 rec_temp.rec_int = coreSize;
1304 set_core_size(nullptr, RECD_INT, rec_temp, nullptr);
1305 found = (REC_RegisterConfigUpdateFunc("proxy.config.core_limit", set_core_size, nullptr) == REC_ERR_OKAY);
1306
1307 ink_assert(found);
1308 }
1309 }
1310
1311 static void
adjust_sys_settings()1312 adjust_sys_settings()
1313 {
1314 struct rlimit lim;
1315 int fds_throttle = -1;
1316 rlim_t maxfiles;
1317
1318 maxfiles = ink_get_max_files();
1319 if (maxfiles != RLIM_INFINITY) {
1320 float file_max_pct = 0.9;
1321
1322 REC_ReadConfigFloat(file_max_pct, "proxy.config.system.file_max_pct");
1323 if (file_max_pct > 1.0) {
1324 file_max_pct = 1.0;
1325 }
1326
1327 lim.rlim_cur = lim.rlim_max = static_cast<rlim_t>(maxfiles * file_max_pct);
1328 if (setrlimit(RLIMIT_NOFILE, &lim) == 0 && getrlimit(RLIMIT_NOFILE, &lim) == 0) {
1329 fds_limit = static_cast<int>(lim.rlim_cur);
1330 syslog(LOG_NOTICE, "NOTE: RLIMIT_NOFILE(%d):cur(%d),max(%d)", RLIMIT_NOFILE, static_cast<int>(lim.rlim_cur),
1331 static_cast<int>(lim.rlim_max));
1332 }
1333 }
1334
1335 REC_ReadConfigInteger(fds_throttle, "proxy.config.net.connections_throttle");
1336
1337 if (getrlimit(RLIMIT_NOFILE, &lim) == 0) {
1338 if (fds_throttle > (int)(lim.rlim_cur - THROTTLE_FD_HEADROOM)) {
1339 lim.rlim_cur = (lim.rlim_max = (rlim_t)(fds_throttle + THROTTLE_FD_HEADROOM));
1340 if (setrlimit(RLIMIT_NOFILE, &lim) == 0 && getrlimit(RLIMIT_NOFILE, &lim) == 0) {
1341 fds_limit = static_cast<int>(lim.rlim_cur);
1342 syslog(LOG_NOTICE, "NOTE: RLIMIT_NOFILE(%d):cur(%d),max(%d)", RLIMIT_NOFILE, static_cast<int>(lim.rlim_cur),
1343 static_cast<int>(lim.rlim_max));
1344 }
1345 }
1346 }
1347
1348 ink_max_out_rlimit(RLIMIT_STACK);
1349 ink_max_out_rlimit(RLIMIT_DATA);
1350 ink_max_out_rlimit(RLIMIT_FSIZE);
1351
1352 #ifdef RLIMIT_RSS
1353 ink_max_out_rlimit(RLIMIT_RSS);
1354 #endif
1355 }
1356
1357 struct ShowStats : public Continuation {
1358 #ifdef ENABLE_TIME_TRACE
1359 FILE *fp;
1360 #endif
1361 int cycle = 0;
1362 int64_t last_cc = 0;
1363 int64_t last_rb = 0;
1364 int64_t last_w = 0;
1365 int64_t last_r = 0;
1366 int64_t last_wb = 0;
1367 int64_t last_nrb = 0;
1368 int64_t last_nw = 0;
1369 int64_t last_nr = 0;
1370 int64_t last_nwb = 0;
1371 int64_t last_p = 0;
1372 int64_t last_o = 0;
1373 int
mainEventShowStats1374 mainEvent(int event, Event *e)
1375 {
1376 (void)event;
1377 (void)e;
1378 if (!(cycle++ % 24)) {
1379 printf("r:rr w:ww r:rbs w:wbs open polls\n");
1380 }
1381 int64_t sval, cval;
1382
1383 NET_READ_DYN_SUM(net_calls_to_readfromnet_stat, sval);
1384 int64_t d_rb = sval - last_rb;
1385 last_rb += d_rb;
1386 NET_READ_DYN_SUM(net_calls_to_readfromnet_afterpoll_stat, sval);
1387 int64_t d_r = sval - last_r;
1388 last_r += d_r;
1389
1390 NET_READ_DYN_SUM(net_calls_to_writetonet_stat, sval);
1391 int64_t d_wb = sval - last_wb;
1392 last_wb += d_wb;
1393 NET_READ_DYN_SUM(net_calls_to_writetonet_afterpoll_stat, sval);
1394 int64_t d_w = sval - last_w;
1395 last_w += d_w;
1396
1397 NET_READ_DYN_STAT(net_read_bytes_stat, sval, cval);
1398 int64_t d_nrb = sval - last_nrb;
1399 last_nrb += d_nrb;
1400 int64_t d_nr = cval - last_nr;
1401 last_nr += d_nr;
1402
1403 NET_READ_DYN_STAT(net_write_bytes_stat, sval, cval);
1404 int64_t d_nwb = sval - last_nwb;
1405 last_nwb += d_nwb;
1406 int64_t d_nw = cval - last_nw;
1407 last_nw += d_nw;
1408
1409 NET_READ_GLOBAL_DYN_SUM(net_connections_currently_open_stat, sval);
1410 int64_t d_o = sval;
1411
1412 NET_READ_DYN_STAT(net_handler_run_stat, sval, cval);
1413 int64_t d_p = cval - last_p;
1414 last_p += d_p;
1415 printf("%" PRId64 ":%" PRId64 " %" PRId64 ":%" PRId64 " %" PRId64 ":%" PRId64 " %" PRId64 ":%" PRId64 " %" PRId64 " %" PRId64
1416 "\n",
1417 d_rb, d_r, d_wb, d_w, d_nrb, d_nr, d_nwb, d_nw, d_o, d_p);
1418 #ifdef ENABLE_TIME_TRACE
1419 int i;
1420 fprintf(fp, "immediate_events_time_dist\n");
1421 for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
1422 if ((i % 10) == 0)
1423 fprintf(fp, "\n");
1424 fprintf(fp, "%5d ", immediate_events_time_dist[i]);
1425 }
1426 fprintf(fp, "\ncnt_immediate_events=%d\n", cnt_immediate_events);
1427
1428 fprintf(fp, "cdb_callback_time_dist\n");
1429 for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
1430 if ((i % 10) == 0)
1431 fprintf(fp, "\n");
1432 fprintf(fp, "%5d ", cdb_callback_time_dist[i]);
1433 }
1434 fprintf(fp, "\ncdb_cache_callbacks=%d\n", cdb_cache_callbacks);
1435
1436 fprintf(fp, "callback_time_dist\n");
1437 for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
1438 if ((i % 10) == 0)
1439 printf("\n");
1440 fprintf(fp, "%5d ", callback_time_dist[i]);
1441 }
1442 fprintf(fp, "\ncache_callbacks=%d\n", cache_callbacks);
1443
1444 fprintf(fp, "rmt_callback_time_dist\n");
1445 for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
1446 if ((i % 10) == 0)
1447 fprintf(fp, "\n");
1448 fprintf(fp, "%5d ", rmt_callback_time_dist[i]);
1449 }
1450 fprintf(fp, "\nrmt_cache_callbacks=%d\n", rmt_cache_callbacks);
1451
1452 fprintf(fp, "inmsg_time_dist\n");
1453 for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
1454 if ((i % 10) == 0)
1455 fprintf(fp, "\n");
1456 fprintf(fp, "%5d ", inmsg_time_dist[i]);
1457 }
1458 fprintf(fp, "\ninmsg_events=%d\n", inmsg_events);
1459
1460 fprintf(fp, "open_delay_time_dist\n");
1461 for (i = 0; i < TIME_DIST_BUCKETS_SIZE; i++) {
1462 if ((i % 10) == 0)
1463 fprintf(fp, "\n");
1464 fprintf(fp, "%5d ", open_delay_time_dist[i]);
1465 }
1466 fprintf(fp, "\nopen_delay_events=%d\n", open_delay_events);
1467
1468 fflush(fp);
1469 #endif
1470 return EVENT_CONT;
1471 }
ShowStatsShowStats1472 ShowStats() : Continuation(nullptr)
1473
1474 {
1475 SET_HANDLER(&ShowStats::mainEvent);
1476 #ifdef ENABLE_TIME_TRACE
1477 fp = fopen("./time_trace.out", "a");
1478 #endif
1479 }
1480 };
1481
1482 // static void syslog_log_configure()
1483 //
1484 // Reads the syslog configuration variable
1485 // and sets the global integer for the
1486 // facility and calls open log with the
1487 // new facility
1488 //
1489 static void
syslog_log_configure()1490 syslog_log_configure()
1491 {
1492 bool found = false;
1493 char sys_var[] = "proxy.config.syslog_facility";
1494 char *facility_str = REC_readString(sys_var, &found);
1495
1496 if (found) {
1497 int facility = facility_string_to_int(facility_str);
1498
1499 ats_free(facility_str);
1500 if (facility < 0) {
1501 syslog(LOG_WARNING, "Bad syslog facility in %s. Keeping syslog at LOG_DAEMON", ts::filename::RECORDS);
1502 } else {
1503 Debug("server", "Setting syslog facility to %d", facility);
1504 closelog();
1505 openlog("traffic_server", LOG_PID | LOG_NDELAY | LOG_NOWAIT, facility);
1506 }
1507 } else {
1508 syslog(LOG_WARNING, "Missing syslog facility config %s. Keeping syslog at LOG_DAEMON", sys_var);
1509 }
1510 }
1511
1512 static void
check_system_constants()1513 check_system_constants()
1514 {
1515 }
1516
1517 static void
init_http_header()1518 init_http_header()
1519 {
1520 url_init();
1521 mime_init();
1522 http_init();
1523 hpack_huffman_init();
1524 }
1525
1526 #if TS_HAS_TESTS
1527 struct RegressionCont : public Continuation {
1528 int initialized = 0;
1529 int waits = 0;
1530 int started = 0;
1531
1532 int
mainEventRegressionCont1533 mainEvent(int event, Event *e)
1534 {
1535 (void)event;
1536 (void)e;
1537 int res = 0;
1538 if (!initialized && (cacheProcessor.IsCacheEnabled() != CACHE_INITIALIZED)) {
1539 printf("Regression waiting for the cache to be ready... %d\n", ++waits);
1540 return EVENT_CONT;
1541 }
1542
1543 char *rt = const_cast<char *>(regression_test[0] == 0 ? "" : regression_test);
1544 if (!initialized && RegressionTest::run(rt, regression_level) == REGRESSION_TEST_INPROGRESS) {
1545 initialized = 1;
1546 return EVENT_CONT;
1547 }
1548
1549 if ((res = RegressionTest::check_status(regression_level)) == REGRESSION_TEST_INPROGRESS) {
1550 return EVENT_CONT;
1551 }
1552
1553 TSSystemState::shut_down_event_system();
1554 fprintf(stderr, "REGRESSION_TEST DONE: %s\n", regression_status_string(res));
1555 ::exit(res == REGRESSION_TEST_PASSED ? 0 : 1);
1556 return EVENT_CONT;
1557 }
1558
RegressionContRegressionCont1559 RegressionCont() : Continuation(new_ProxyMutex()) { SET_HANDLER(&RegressionCont::mainEvent); }
1560 };
1561
1562 static void
run_RegressionTest()1563 run_RegressionTest()
1564 {
1565 if (regression_level) {
1566 eventProcessor.schedule_every(new RegressionCont(), HRTIME_SECONDS(1));
1567 }
1568 }
1569 #endif // TS_HAS_TESTS
1570
1571 static void
chdir_root()1572 chdir_root()
1573 {
1574 std::string prefix = Layout::get()->prefix;
1575
1576 if (chdir(prefix.c_str()) < 0) {
1577 fprintf(stderr, "%s: unable to change to root directory \"%s\" [%d '%s']\n", appVersionInfo.AppStr, prefix.c_str(), errno,
1578 strerror(errno));
1579 fprintf(stderr, "%s: please correct the path or set the TS_ROOT environment variable\n", appVersionInfo.AppStr);
1580 ::exit(1);
1581 } else {
1582 printf("%s: using root directory '%s'\n", appVersionInfo.AppStr, prefix.c_str());
1583 }
1584 }
1585
1586 static int
adjust_num_of_net_threads(int nthreads)1587 adjust_num_of_net_threads(int nthreads)
1588 {
1589 float autoconfig_scale = 1.0;
1590 int nth_auto_config = 1;
1591 int num_of_threads_tmp = 1;
1592
1593 REC_ReadConfigInteger(nth_auto_config, "proxy.config.exec_thread.autoconfig");
1594
1595 Debug("threads", "initial number of net threads is %d", nthreads);
1596 Debug("threads", "net threads auto-configuration %s", nth_auto_config ? "enabled" : "disabled");
1597
1598 if (!nth_auto_config) {
1599 REC_ReadConfigInteger(num_of_threads_tmp, "proxy.config.exec_thread.limit");
1600
1601 if (num_of_threads_tmp <= 0) {
1602 num_of_threads_tmp = 1;
1603 } else if (num_of_threads_tmp > MAX_EVENT_THREADS) {
1604 num_of_threads_tmp = MAX_EVENT_THREADS;
1605 }
1606
1607 nthreads = num_of_threads_tmp;
1608 } else { /* autoconfig is enabled */
1609 num_of_threads_tmp = nthreads;
1610 REC_ReadConfigFloat(autoconfig_scale, "proxy.config.exec_thread.autoconfig.scale");
1611 num_of_threads_tmp = static_cast<int>(static_cast<float>(num_of_threads_tmp) * autoconfig_scale);
1612
1613 if (unlikely(num_of_threads_tmp > MAX_EVENT_THREADS)) {
1614 num_of_threads_tmp = MAX_EVENT_THREADS;
1615 }
1616
1617 if (num_of_threads_tmp) {
1618 nthreads = num_of_threads_tmp;
1619 }
1620 }
1621
1622 if (unlikely(nthreads <= 0)) { /* impossible case -just for protection */
1623 Warning("number of net threads must be greater than 0, resetting to 1");
1624 nthreads = 1;
1625 }
1626
1627 Debug("threads", "adjusted number of net threads is %d", nthreads);
1628 return nthreads;
1629 }
1630
1631 /**
1632 * Change the uid and gid to what is in the passwd entry for supplied user name.
1633 * @param user User name in the passwd file to change the uid and gid to.
1634 */
1635 static void
change_uid_gid(const char * user)1636 change_uid_gid(const char *user)
1637 {
1638 #if !TS_USE_POSIX_CAP
1639 RecInt enabled;
1640
1641 if (RecGetRecordInt("proxy.config.ssl.cert.load_elevated", &enabled) == REC_ERR_OKAY && enabled) {
1642 Warning("ignoring proxy.config.ssl.cert.load_elevated because Traffic Server was built without POSIX capabilities support");
1643 }
1644
1645 if (RecGetRecordInt("proxy.config.plugin.load_elevated", &enabled) == REC_ERR_OKAY && enabled) {
1646 Warning("ignoring proxy.config.plugin.load_elevated because Traffic Server was built without POSIX capabilities support");
1647 }
1648 #endif /* TS_USE_POSIX_CAP */
1649
1650 // This is primarily for regression tests, where people just run "traffic_server -R1" as a regular user. Dropping
1651 // privilege is never going to succeed unless we were privileged in the first place. I guess we ought to check
1652 // capabilities as well :-/
1653 if (getuid() != 0 && geteuid() != 0) {
1654 Note("Traffic Server is running unprivileged, not switching to user '%s'", user);
1655 return;
1656 }
1657
1658 Debug("privileges", "switching to unprivileged user '%s'", user);
1659 ImpersonateUser(user, IMPERSONATE_PERMANENT);
1660
1661 #if !defined(BIG_SECURITY_HOLE) || (BIG_SECURITY_HOLE != 0)
1662 if (getuid() == 0 || geteuid() == 0) {
1663 ink_fatal("Trafficserver has not been designed to serve pages while\n"
1664 "\trunning as root. There are known race conditions that\n"
1665 "\twill allow any local user to read any file on the system.\n"
1666 "\tIf you still desire to serve pages as root then\n"
1667 "\tadd -DBIG_SECURITY_HOLE to the CFLAGS env variable\n"
1668 "\tand then rebuild the server.\n"
1669 "\tIt is strongly suggested that you instead modify the\n"
1670 "\tproxy.config.admin.user_id directive in your\n"
1671 "\t%s file to list a non-root user.\n",
1672 ts::filename::RECORDS);
1673 }
1674 #endif
1675 }
1676
1677 /*
1678 * Binds stdout and stderr to files specified by the parameters
1679 *
1680 * On failure to bind, emits a warning and whatever is being bound
1681 * just isn't bound
1682 *
1683 * This must work without the ability to elevate privilege if the files are accessible without.
1684 */
1685 void
bind_outputs(const char * bind_stdout_p,const char * bind_stderr_p)1686 bind_outputs(const char *bind_stdout_p, const char *bind_stderr_p)
1687 {
1688 int log_fd;
1689 unsigned int flags = O_WRONLY | O_APPEND | O_CREAT | O_SYNC;
1690
1691 if (*bind_stdout_p != 0) {
1692 Debug("log", "binding stdout to %s", bind_stdout_p);
1693 log_fd = elevating_open(bind_stdout_p, flags, 0644);
1694 if (log_fd < 0) {
1695 fprintf(stdout, "[Warning]: TS unable to open log file \"%s\" [%d '%s']\n", bind_stdout_p, errno, strerror(errno));
1696 } else {
1697 Debug("log", "duping stdout");
1698 dup2(log_fd, STDOUT_FILENO);
1699 close(log_fd);
1700 }
1701 }
1702 if (*bind_stderr_p != 0) {
1703 Debug("log", "binding stderr to %s", bind_stderr_p);
1704 log_fd = elevating_open(bind_stderr_p, O_WRONLY | O_APPEND | O_CREAT | O_SYNC, 0644);
1705 if (log_fd < 0) {
1706 fprintf(stdout, "[Warning]: TS unable to open log file \"%s\" [%d '%s']\n", bind_stderr_p, errno, strerror(errno));
1707 } else {
1708 Debug("log", "duping stderr");
1709 dup2(log_fd, STDERR_FILENO);
1710 close(log_fd);
1711 }
1712 }
1713 }
1714
1715 //
1716 // Main
1717 //
1718
1719 int
main(int,const char ** argv)1720 main(int /* argc ATS_UNUSED */, const char **argv)
1721 {
1722 #if TS_HAS_PROFILER
1723 HeapProfilerStart("/tmp/ts.hprof");
1724 ProfilerStart("/tmp/ts.prof");
1725 #endif
1726 bool admin_user_p = false;
1727
1728 #if defined(DEBUG) && defined(HAVE_MCHECK_PEDANTIC)
1729 mcheck_pedantic(NULL);
1730 #endif
1731
1732 pcre_malloc = ats_malloc;
1733 pcre_free = ats_free;
1734
1735 // Verify system dependent 'constants'
1736 check_system_constants();
1737
1738 // Define the version info
1739 appVersionInfo.setup(PACKAGE_NAME, "traffic_server", PACKAGE_VERSION, __DATE__, __TIME__, BUILD_MACHINE, BUILD_PERSON, "");
1740
1741 runroot_handler(argv);
1742 // Before accessing file system initialize Layout engine
1743 Layout::create();
1744 // Let's be clear on what exactly is starting up.
1745 printf("Traffic Server " PACKAGE_VERSION BUILD_NUMBER " " __DATE__ " " __TIME__ " " BUILD_MACHINE "\n");
1746 chdir_root(); // change directory to the install root of traffic server.
1747
1748 std::sort(argument_descriptions, argument_descriptions + countof(argument_descriptions),
1749 [](ArgumentDescription const &a, ArgumentDescription const &b) { return 0 > strcasecmp(a.name, b.name); });
1750
1751 process_args(&appVersionInfo, argument_descriptions, countof(argument_descriptions), argv);
1752 command_flag = command_flag || *command_string;
1753 command_index = find_cmd_index(command_string);
1754 command_valid = command_flag && command_index >= 0;
1755
1756 ink_freelist_init_ops(cmd_disable_freelist, cmd_disable_pfreelist);
1757
1758 #if TS_HAS_TESTS
1759 if (regression_list) {
1760 RegressionTest::list();
1761 ::exit(0);
1762 }
1763 #endif
1764
1765 // Bootstrap syslog. Since we haven't read records.config
1766 // yet we do not know where
1767 openlog("traffic_server", LOG_PID | LOG_NDELAY | LOG_NOWAIT, LOG_DAEMON);
1768
1769 // Setup Diags temporary to allow librecords to be initialized.
1770 // We will re-configure Diags again with proper configurations after
1771 // librecords initialized. This is needed because:
1772 // - librecords needs diags to initialize
1773 // - diags needs to read some configuration records to initial
1774 // We cannot mimic whatever TM did (start Diag, init. librecords, and
1775 // re-start Diag completely) because at initialize, TM only has 1 thread.
1776 // In TS, some threads have already created, so if we delete Diag and
1777 // re-start it again, TS will crash.
1778 // This is also needed for log rotation - setting up the file can cause privilege
1779 // related errors and if diagsConfig isn't get up yet that will crash on a NULL pointer.
1780 diagsConfig = new DiagsConfig("Server", DIAGS_LOG_FILENAME, error_tags, action_tags, false);
1781 diags = diagsConfig->diags;
1782 diags->set_std_output(StdStream::STDOUT, bind_stdout);
1783 diags->set_std_output(StdStream::STDERR, bind_stderr);
1784 if (is_debug_tag_set("diags")) {
1785 diags->dump();
1786 }
1787
1788 // Bind stdout and stderr to specified switches
1789 // Still needed despite the set_std{err,out}_output() calls later since there are
1790 // fprintf's before those calls
1791 bind_outputs(bind_stdout, bind_stderr);
1792
1793 // Local process manager
1794 initialize_process_manager();
1795
1796 // Set the core limit for the process
1797 init_core_size();
1798 init_system();
1799
1800 // Adjust system and process settings
1801 adjust_sys_settings();
1802
1803 // Restart syslog now that we have configuration info
1804 syslog_log_configure();
1805
1806 // Register stats if standalone
1807 if (DEFAULT_REMOTE_MANAGEMENT_FLAG == remote_management_flag) {
1808 RecRegisterStatInt(RECT_NODE, "proxy.node.config.reconfigure_time", time(nullptr), RECP_NON_PERSISTENT);
1809 RecRegisterStatInt(RECT_NODE, "proxy.node.config.reconfigure_required", 0, RECP_NON_PERSISTENT);
1810 RecRegisterStatInt(RECT_NODE, "proxy.node.config.restart_required.proxy", 0, RECP_NON_PERSISTENT);
1811 RecRegisterStatInt(RECT_NODE, "proxy.node.config.restart_required.manager", 0, RECP_NON_PERSISTENT);
1812 RecRegisterStatInt(RECT_NODE, "proxy.node.config.draining", 0, RECP_NON_PERSISTENT);
1813 }
1814
1815 // init huge pages
1816 int enabled;
1817 REC_ReadConfigInteger(enabled, "proxy.config.allocator.hugepages");
1818 ats_hugepage_init(enabled);
1819 Debug("hugepages", "ats_pagesize reporting %zu", ats_pagesize());
1820 Debug("hugepages", "ats_hugepage_size reporting %zu", ats_hugepage_size());
1821
1822 if (!num_accept_threads) {
1823 REC_ReadConfigInteger(num_accept_threads, "proxy.config.accept_threads");
1824 }
1825
1826 if (!num_task_threads) {
1827 REC_ReadConfigInteger(num_task_threads, "proxy.config.task_threads");
1828 }
1829
1830 ats_scoped_str user(MAX_LOGIN + 1);
1831
1832 *user = '\0';
1833 admin_user_p = ((REC_ERR_OKAY == REC_ReadConfigString(user, "proxy.config.admin.user_id", MAX_LOGIN)) && (*user != '\0') &&
1834 (0 != strcmp(user, "#-1")));
1835
1836 // Set up crash logging. We need to do this while we are still privileged so that the crash
1837 // logging helper runs as root. Don't bother setting up a crash logger if we are going into
1838 // command mode since that's not going to daemonize or run for a long time unattended.
1839 if (!command_flag) {
1840 crash_logger_init(user);
1841 signal_register_crash_handler(crash_logger_invoke);
1842 }
1843
1844 #if TS_USE_POSIX_CAP
1845 // Change the user of the process.
1846 // Do this before we start threads so we control the user id of the
1847 // threads (rather than have it change asynchronously during thread
1848 // execution). We also need to do this before we fiddle with capabilities
1849 // as those are thread local and if we change the user id it will
1850 // modify the capabilities in other threads, breaking things.
1851 if (admin_user_p) {
1852 PreserveCapabilities();
1853 change_uid_gid(user);
1854 RestrictCapabilities();
1855 }
1856 #endif
1857
1858 // Ensure only one copy of traffic server is running, unless it's a command
1859 // that doesn't require a lock.
1860 if (!(command_valid && commands[command_index].no_process_lock)) {
1861 check_lockfile();
1862 }
1863
1864 // Can't generate a log message yet, do that right after Diags is
1865 // setup.
1866
1867 // This call is required for win_9xMe
1868 // without this this_ethread() is failing when
1869 // start_HttpProxyServer is called from main thread
1870 Thread *main_thread = new EThread;
1871 main_thread->set_specific();
1872
1873 // Re-initialize diagsConfig based on records.config configuration
1874 DiagsConfig *old_log = diagsConfig;
1875 diagsConfig = new DiagsConfig("Server", DIAGS_LOG_FILENAME, error_tags, action_tags, true);
1876 diags = diagsConfig->diags;
1877 RecSetDiags(diags);
1878 diags->set_std_output(StdStream::STDOUT, bind_stdout);
1879 diags->set_std_output(StdStream::STDERR, bind_stderr);
1880 if (is_debug_tag_set("diags")) {
1881 diags->dump();
1882 }
1883
1884 if (old_log) {
1885 delete (old_log);
1886 old_log = nullptr;
1887 }
1888
1889 DebugCapabilities("privileges"); // Can do this now, logging is up.
1890
1891 // Check if we should do mlockall()
1892 #if defined(MCL_FUTURE)
1893 int mlock_flags = 0;
1894 REC_ReadConfigInteger(mlock_flags, "proxy.config.mlock_enabled");
1895
1896 if (mlock_flags == 2) {
1897 if (0 != mlockall(MCL_CURRENT | MCL_FUTURE)) {
1898 Warning("Unable to mlockall() on startup");
1899 } else {
1900 Debug("server", "Successfully called mlockall()");
1901 }
1902 }
1903 #endif
1904
1905 // setup callback for tracking remap included files
1906 load_remap_file_cb = load_config_file_callback;
1907
1908 // We need to do this early so we can initialize the Machine
1909 // singleton, which depends on configuration values loaded in this.
1910 // We want to initialize Machine as early as possible because it
1911 // has other dependencies. Hopefully not in prep_HttpProxyServer().
1912 HttpConfig::startup();
1913 #if TS_USE_QUIC == 1
1914 Http3Config::startup();
1915 #endif
1916
1917 /* Set up the machine with the outbound address if that's set,
1918 or the inbound address if set, otherwise let it default.
1919 */
1920 IpEndpoint machine_addr;
1921 ink_zero(machine_addr);
1922 if (HttpConfig::m_master.outbound_ip4.isValid()) {
1923 machine_addr.assign(HttpConfig::m_master.outbound_ip4);
1924 } else if (HttpConfig::m_master.outbound_ip6.isValid()) {
1925 machine_addr.assign(HttpConfig::m_master.outbound_ip6);
1926 } else if (HttpConfig::m_master.inbound_ip4.isValid()) {
1927 machine_addr.assign(HttpConfig::m_master.inbound_ip4);
1928 } else if (HttpConfig::m_master.inbound_ip6.isValid()) {
1929 machine_addr.assign(HttpConfig::m_master.inbound_ip6);
1930 }
1931 Machine::init(nullptr, &machine_addr.sa);
1932
1933 RecRegisterStatString(RECT_PROCESS, "proxy.process.version.server.uuid", (char *)Machine::instance()->uuid.getString(),
1934 RECP_NON_PERSISTENT);
1935
1936 // pmgmt->start() must occur after initialization of Diags but
1937 // before calling RecProcessInit()
1938
1939 REC_ReadConfigInteger(res_track_memory, "proxy.config.res_track_memory");
1940
1941 init_http_header();
1942 ts_session_protocol_well_known_name_indices_init();
1943
1944 // Sanity checks
1945 check_fd_limit();
1946
1947 // Alter the frequencies at which the update threads will trigger
1948 #define SET_INTERVAL(scope, name, var) \
1949 do { \
1950 RecInt tmpint; \
1951 Debug("statsproc", "Looking for %s", name); \
1952 if (RecGetRecordInt(name, &tmpint) == REC_ERR_OKAY) { \
1953 Debug("statsproc", "Found %s", name); \
1954 scope##_set_##var(tmpint); \
1955 } \
1956 } while (0)
1957 SET_INTERVAL(RecProcess, "proxy.config.config_update_interval_ms", config_update_interval_ms);
1958 SET_INTERVAL(RecProcess, "proxy.config.raw_stat_sync_interval_ms", raw_stat_sync_interval_ms);
1959 SET_INTERVAL(RecProcess, "proxy.config.remote_sync_interval_ms", remote_sync_interval_ms);
1960
1961 // Initialize the stat pages manager
1962 statPagesManager.init();
1963
1964 num_of_net_threads = adjust_num_of_net_threads(num_of_net_threads);
1965
1966 size_t stacksize;
1967 REC_ReadConfigInteger(stacksize, "proxy.config.thread.default.stacksize");
1968
1969 // This has some special semantics, in that providing this configuration on
1970 // command line has higher priority than what is set in records.config.
1971 if (-1 != poll_timeout) {
1972 net_config_poll_timeout = poll_timeout;
1973 } else {
1974 REC_ReadConfigInteger(net_config_poll_timeout, "proxy.config.net.poll_timeout");
1975 }
1976
1977 // This shouldn't happen, but lets make sure we run somewhat reasonable.
1978 if (net_config_poll_timeout < 0) {
1979 net_config_poll_timeout = 10; // Default value for all platform.
1980 }
1981
1982 REC_ReadConfigInteger(thread_max_heartbeat_mseconds, "proxy.config.thread.max_heartbeat_mseconds");
1983
1984 ink_event_system_init(ts::ModuleVersion(1, 0, ts::ModuleVersion::PRIVATE));
1985 ink_net_init(ts::ModuleVersion(1, 0, ts::ModuleVersion::PRIVATE));
1986 ink_aio_init(ts::ModuleVersion(1, 0, ts::ModuleVersion::PRIVATE));
1987 ink_cache_init(ts::ModuleVersion(1, 0, ts::ModuleVersion::PRIVATE));
1988 ink_hostdb_init(
1989 ts::ModuleVersion(HOSTDB_MODULE_INTERNAL_VERSION._major, HOSTDB_MODULE_INTERNAL_VERSION._minor, ts::ModuleVersion::PRIVATE));
1990 ink_dns_init(
1991 ts::ModuleVersion(HOSTDB_MODULE_INTERNAL_VERSION._major, HOSTDB_MODULE_INTERNAL_VERSION._minor, ts::ModuleVersion::PRIVATE));
1992 ink_split_dns_init(ts::ModuleVersion(1, 0, ts::ModuleVersion::PRIVATE));
1993
1994 naVecMutex = new_ProxyMutex();
1995
1996 // Do the inits for NetProcessors that use ET_NET threads. MUST be before starting those threads.
1997 netProcessor.init();
1998 prep_HttpProxyServer();
1999
2000 #if TS_USE_QUIC == 1
2001 // OK, pushing a spawn scheduling here
2002 quic_NetProcessor.init();
2003 #endif
2004
2005 // If num_accept_threads == 0, let the ET_NET threads set the condition variable,
2006 // Else we set it here so when checking the condition variable later it returns immediately.
2007 if (num_accept_threads == 0 || command_flag) {
2008 eventProcessor.thread_group[ET_NET]._afterStartCallback = init_HttpProxyServer;
2009 } else {
2010 std::unique_lock<std::mutex> lock(proxyServerMutex);
2011 et_net_threads_ready = true;
2012 lock.unlock();
2013 proxyServerCheck.notify_one();
2014 }
2015
2016 // !! ET_NET threads start here !!
2017 // This means any spawn scheduling must be done before this point.
2018 eventProcessor.start(num_of_net_threads, stacksize);
2019
2020 eventProcessor.schedule_every(new SignalContinuation, HRTIME_MSECOND * 500, ET_CALL);
2021 eventProcessor.schedule_every(new DiagsLogContinuation, HRTIME_SECOND, ET_TASK);
2022 eventProcessor.schedule_every(new MemoryLimit, HRTIME_SECOND * 10, ET_TASK);
2023 REC_RegisterConfigUpdateFunc("proxy.config.dump_mem_info_frequency", init_memory_tracker, nullptr);
2024 init_memory_tracker(nullptr, RECD_NULL, RecData(), nullptr);
2025
2026 char *p = REC_ConfigReadString("proxy.config.diags.debug.client_ip");
2027 if (p) {
2028 // Translate string to IpAddr
2029 set_debug_ip(p);
2030 }
2031 REC_RegisterConfigUpdateFunc("proxy.config.diags.debug.client_ip", update_debug_client_ip, nullptr);
2032
2033 // log initialization moved down
2034
2035 if (command_flag) {
2036 int cmd_ret = cmd_mode();
2037
2038 if (cmd_ret != CMD_IN_PROGRESS) {
2039 // Check the condition variable.
2040 {
2041 std::unique_lock<std::mutex> lock(proxyServerMutex);
2042 proxyServerCheck.wait(lock, [] { return et_net_threads_ready; });
2043 }
2044
2045 if (cmd_ret >= 0) {
2046 ::exit(0); // everything is OK
2047 } else {
2048 ::exit(1); // in error
2049 }
2050 }
2051 } else {
2052 RecProcessStart();
2053 initCacheControl();
2054 IpAllow::startup();
2055 HostStatus::instance().loadHostStatusFromStats();
2056 netProcessor.init_socks();
2057 ParentConfig::startup();
2058 SplitDNSConfig::startup();
2059
2060 // Initialize HTTP/2
2061 Http2::init();
2062 #if TS_USE_QUIC == 1
2063 // Initialize HTTP/QUIC
2064 Http3::init();
2065 #endif
2066
2067 if (!HttpProxyPort::loadValue(http_accept_port_descriptor)) {
2068 HttpProxyPort::loadConfig();
2069 }
2070 HttpProxyPort::loadDefaultIfEmpty();
2071
2072 dnsProcessor.start(0, stacksize);
2073 if (hostDBProcessor.start() < 0)
2074 SignalWarning(MGMT_SIGNAL_SYSTEM_ERROR, "bad hostdb or storage configuration, hostdb disabled");
2075
2076 // initialize logging (after event and net processor)
2077 Log::init(remote_management_flag ? 0 : Log::NO_REMOTE_MANAGEMENT);
2078
2079 (void)parsePluginConfig();
2080
2081 // Init plugins as soon as logging is ready.
2082 (void)plugin_init(); // plugin.config
2083
2084 SSLConfigParams::init_ssl_ctx_cb = init_ssl_ctx_callback;
2085 SSLConfigParams::load_ssl_file_cb = load_ssl_file_callback;
2086 sslNetProcessor.start(-1, stacksize);
2087 #if TS_USE_QUIC == 1
2088 quic_NetProcessor.start(-1, stacksize);
2089 #endif
2090 pmgmt->registerPluginCallbacks(global_config_cbs);
2091
2092 cacheProcessor.afterInitCallbackSet(&CB_After_Cache_Init);
2093 cacheProcessor.start();
2094
2095 // UDP net-threads are turned off by default.
2096 if (!num_of_udp_threads) {
2097 REC_ReadConfigInteger(num_of_udp_threads, "proxy.config.udp.threads");
2098 }
2099 if (num_of_udp_threads) {
2100 udpNet.start(num_of_udp_threads, stacksize);
2101 eventProcessor.thread_group[ET_UDP]._afterStartCallback = init_HttpProxyServer;
2102 }
2103
2104 // Initialize Response Body Factory
2105 body_factory = new HttpBodyFactory;
2106
2107 // Continuation Statistics Dump
2108 if (show_statistics) {
2109 eventProcessor.schedule_every(new ShowStats(), HRTIME_SECONDS(show_statistics), ET_CALL);
2110 }
2111
2112 //////////////////////////////////////
2113 // main server logic initiated here //
2114 //////////////////////////////////////
2115
2116 init_accept_HttpProxyServer(num_accept_threads);
2117 transformProcessor.start();
2118
2119 int http_enabled = 1;
2120 REC_ReadConfigInteger(http_enabled, "proxy.config.http.enabled");
2121
2122 if (http_enabled) {
2123 // call the ready hooks before we start accepting connections.
2124 APIHook *hook = lifecycle_hooks->get(TS_LIFECYCLE_PORTS_INITIALIZED_HOOK);
2125 while (hook) {
2126 hook->invoke(TS_EVENT_LIFECYCLE_PORTS_INITIALIZED, nullptr);
2127 hook = hook->next();
2128 }
2129
2130 int delay_p = 0;
2131 REC_ReadConfigInteger(delay_p, "proxy.config.http.wait_for_cache");
2132
2133 // Check the condition variable.
2134 {
2135 std::unique_lock<std::mutex> lock(proxyServerMutex);
2136 proxyServerCheck.wait(lock, [] { return et_net_threads_ready; });
2137 }
2138
2139 #if TS_USE_QUIC == 1
2140 if (num_of_udp_threads) {
2141 std::unique_lock<std::mutex> lock(etUdpMutex);
2142 etUdpCheck.wait(lock, [] { return et_udp_threads_ready; });
2143 }
2144 #endif
2145 // Delay only if config value set and flag value is zero
2146 // (-1 => cache already initialized)
2147 if (delay_p && ink_atomic_cas(&delay_listen_for_cache, 0, 1)) {
2148 Debug("http_listen", "Delaying listen, waiting for cache initialization");
2149 } else {
2150 // If we've come here, either:
2151 //
2152 // 1. The user did not configure wait_for_cache, and/or
2153 // 2. The previous delay_listen_for_cache value was not 0, thus the cache
2154 // must have been initialized already.
2155 //
2156 // In either case we should not delay to accept the ports.
2157 Debug("http_listen", "Not delaying listen");
2158 start_HttpProxyServer(); // PORTS_READY_HOOK called from in here
2159 emit_fully_initialized_message();
2160 }
2161 }
2162 // Plugins can register their own configuration names so now after they've done that
2163 // check for unexpected names. This is very late because remap plugins must be allowed to
2164 // fire up as well.
2165 RecConfigWarnIfUnregistered();
2166
2167 // "Task" processor, possibly with its own set of task threads
2168 tasksProcessor.register_event_type();
2169 eventProcessor.thread_group[ET_TASK]._afterStartCallback = task_threads_started_callback;
2170 tasksProcessor.start(num_task_threads, stacksize);
2171
2172 if (netProcessor.socks_conf_stuff->accept_enabled) {
2173 start_SocksProxy(netProcessor.socks_conf_stuff->accept_port);
2174 }
2175
2176 pmgmt->registerMgmtCallback(MGMT_EVENT_SHUTDOWN, &mgmt_restart_shutdown_callback);
2177 pmgmt->registerMgmtCallback(MGMT_EVENT_RESTART, &mgmt_restart_shutdown_callback);
2178 pmgmt->registerMgmtCallback(MGMT_EVENT_DRAIN, &mgmt_drain_callback);
2179
2180 // Callback for various storage commands. These all go to the same function so we
2181 // pass the event code along so it can do the right thing. We cast that to <int> first
2182 // just to be safe because the value is a #define, not a typed value.
2183 pmgmt->registerMgmtCallback(MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE, [](ts::MemSpan<void> span) -> void {
2184 mgmt_storage_device_cmd_callback(MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE, span.view());
2185 });
2186 pmgmt->registerMgmtCallback(MGMT_EVENT_LIFECYCLE_MESSAGE, &mgmt_lifecycle_msg_callback);
2187
2188 ink_set_thread_name("[TS_MAIN]");
2189
2190 Note("traffic server running");
2191
2192 #if TS_HAS_TESTS
2193 TransformTest::run();
2194 // run_SimpleHttp();
2195 run_RegressionTest();
2196 #endif
2197
2198 if (getenv("PROXY_AUTO_EXIT")) {
2199 eventProcessor.schedule_in(new AutoStopCont(), HRTIME_SECONDS(atoi(getenv("PROXY_AUTO_EXIT"))));
2200 }
2201 }
2202
2203 #if !TS_USE_POSIX_CAP
2204 if (admin_user_p) {
2205 change_uid_gid(user);
2206 }
2207 #endif
2208
2209 while (!TSSystemState::is_event_system_shut_down()) {
2210 sleep(1);
2211 }
2212
2213 delete main_thread;
2214 }
2215
mgmt_restart_shutdown_callback(ts::MemSpan<void>)2216 static void mgmt_restart_shutdown_callback(ts::MemSpan<void>)
2217 {
2218 sync_cache_dir_on_shutdown();
2219 }
2220
2221 static void
mgmt_drain_callback(ts::MemSpan<void> span)2222 mgmt_drain_callback(ts::MemSpan<void> span)
2223 {
2224 char *arg = span.rebind<char>().data();
2225 TSSystemState::drain(span.size() == 2 && arg[0] == '1');
2226 RecSetRecordInt("proxy.node.config.draining", TSSystemState::is_draining() ? 1 : 0, REC_SOURCE_DEFAULT);
2227 }
2228
2229 static void
mgmt_storage_device_cmd_callback(int cmd,std::string_view const & arg)2230 mgmt_storage_device_cmd_callback(int cmd, std::string_view const &arg)
2231 {
2232 // data is the device name to control
2233 CacheDisk *d = cacheProcessor.find_by_path(arg.data(), int(arg.size()));
2234
2235 if (d) {
2236 switch (cmd) {
2237 case MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE:
2238 Debug("server", "Marking %.*s offline", int(arg.size()), arg.data());
2239 cacheProcessor.mark_storage_offline(d, /* admin */ true);
2240 break;
2241 }
2242 }
2243 }
2244
2245 static void
mgmt_lifecycle_msg_callback(ts::MemSpan<void> span)2246 mgmt_lifecycle_msg_callback(ts::MemSpan<void> span)
2247 {
2248 APIHook *hook = lifecycle_hooks->get(TS_LIFECYCLE_MSG_HOOK);
2249 TSPluginMsg msg;
2250 MgmtInt op;
2251 MgmtMarshallString tag;
2252 MgmtMarshallData payload;
2253 static const MgmtMarshallType fields[] = {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA};
2254
2255 if (mgmt_message_parse(span.data(), span.size(), fields, countof(fields), &op, &tag, &payload) == -1) {
2256 Error("Plugin message - RPC parsing error - message discarded.");
2257 } else {
2258 msg.tag = tag;
2259 msg.data = payload.ptr;
2260 msg.data_size = payload.len;
2261 while (hook) {
2262 TSPluginMsg tmp(msg); // Just to make sure plugins don't mess this up for others.
2263 hook->invoke(TS_EVENT_LIFECYCLE_MSG, &tmp);
2264 hook = hook->next();
2265 }
2266 }
2267 }
2268
2269 static void
init_ssl_ctx_callback(void * ctx,bool server)2270 init_ssl_ctx_callback(void *ctx, bool server)
2271 {
2272 TSEvent event = server ? TS_EVENT_LIFECYCLE_SERVER_SSL_CTX_INITIALIZED : TS_EVENT_LIFECYCLE_CLIENT_SSL_CTX_INITIALIZED;
2273 APIHook *hook =
2274 lifecycle_hooks->get(server ? TS_LIFECYCLE_SERVER_SSL_CTX_INITIALIZED_HOOK : TS_LIFECYCLE_CLIENT_SSL_CTX_INITIALIZED_HOOK);
2275
2276 while (hook) {
2277 hook->invoke(event, ctx);
2278 hook = hook->next();
2279 }
2280 }
2281
2282 static void
load_ssl_file_callback(const char * ssl_file)2283 load_ssl_file_callback(const char *ssl_file)
2284 {
2285 pmgmt->signalConfigFileChild(ts::filename::SSL_MULTICERT, ssl_file);
2286 }
2287
2288 void
load_config_file_callback(const char * parent_file,const char * remap_file)2289 load_config_file_callback(const char *parent_file, const char *remap_file)
2290 {
2291 pmgmt->signalConfigFileChild(parent_file, remap_file);
2292 }
2293
2294 static void
task_threads_started_callback()2295 task_threads_started_callback()
2296 {
2297 APIHook *hook = lifecycle_hooks->get(TS_LIFECYCLE_TASK_THREADS_READY_HOOK);
2298 while (hook) {
2299 WEAK_SCOPED_MUTEX_LOCK(lock, hook->m_cont->mutex, this_ethread());
2300 hook->invoke(TS_EVENT_LIFECYCLE_TASK_THREADS_READY, nullptr);
2301 hook = hook->next();
2302 }
2303 }
2304