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