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