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