1 /** @file
2 
3   traffic_ctl
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 #include "traffic_ctl.h"
25 
26 #include "records/I_RecProcess.h"
27 #include "RecordsConfig.h"
28 #include "tscore/I_Layout.h"
29 #include "tscore/runroot.h"
30 
31 const char *
name() const32 CtrlMgmtRecord::name() const
33 {
34   return this->ele->rec_name;
35 }
36 
37 TSRecordT
type() const38 CtrlMgmtRecord::type() const
39 {
40   return this->ele->rec_type;
41 }
42 
43 int
rclass() const44 CtrlMgmtRecord::rclass() const
45 {
46   return this->ele->rec_class;
47 }
48 
49 int64_t
as_int() const50 CtrlMgmtRecord::as_int() const
51 {
52   switch (this->ele->rec_type) {
53   case TS_REC_INT:
54     return this->ele->valueT.int_val;
55   case TS_REC_COUNTER:
56     return this->ele->valueT.counter_val;
57   default:
58     return 0;
59   }
60 }
61 
62 TSMgmtError
fetch(const char * name)63 CtrlMgmtRecord::fetch(const char *name)
64 {
65   return TSRecordGet(name, this->ele);
66 }
67 
68 TSMgmtError
match(const char * name)69 CtrlMgmtRecordList::match(const char *name)
70 {
71   return TSRecordGetMatchMlt(name, this->list);
72 }
73 
CtrlMgmtRecordValue(const CtrlMgmtRecord & rec)74 CtrlMgmtRecordValue::CtrlMgmtRecordValue(const CtrlMgmtRecord &rec)
75 {
76   this->init(rec.ele->rec_type, rec.ele->valueT);
77 }
78 
CtrlMgmtRecordValue(const TSRecordEle * ele)79 CtrlMgmtRecordValue::CtrlMgmtRecordValue(const TSRecordEle *ele)
80 {
81   this->init(ele->rec_type, ele->valueT);
82 }
83 
CtrlMgmtRecordValue(TSRecordT _t,TSRecordValueT _v)84 CtrlMgmtRecordValue::CtrlMgmtRecordValue(TSRecordT _t, TSRecordValueT _v)
85 {
86   this->init(_t, _v);
87 }
88 
89 void
init(TSRecordT _t,TSRecordValueT _v)90 CtrlMgmtRecordValue::init(TSRecordT _t, TSRecordValueT _v)
91 {
92   this->rec_type = _t;
93   switch (this->rec_type) {
94   case TS_REC_INT:
95     snprintf(this->fmt.nbuf, sizeof(this->fmt.nbuf), "%" PRId64, _v.int_val);
96     break;
97   case TS_REC_COUNTER:
98     snprintf(this->fmt.nbuf, sizeof(this->fmt.nbuf), "%" PRId64, _v.counter_val);
99     break;
100   case TS_REC_FLOAT:
101     snprintf(this->fmt.nbuf, sizeof(this->fmt.nbuf), "%f", _v.float_val);
102     break;
103   case TS_REC_STRING:
104     if (strcmp(_v.string_val, "") == 0) {
105       this->fmt.str = "\"\"";
106     } else {
107       this->fmt.str = _v.string_val;
108     }
109     break;
110   default:
111     rec_type      = TS_REC_STRING;
112     this->fmt.str = "(invalid)";
113   }
114 }
115 
116 const char *
c_str() const117 CtrlMgmtRecordValue::c_str() const
118 {
119   switch (this->rec_type) {
120   case TS_REC_STRING:
121     return this->fmt.str;
122   default:
123     return this->fmt.nbuf;
124   }
125 }
126 
127 void
CtrlMgmtError(TSMgmtError err,const char * fmt,...)128 CtrlMgmtError(TSMgmtError err, const char *fmt, ...)
129 {
130   ats_scoped_str msg(TSGetErrorMessage(err));
131 
132   if (fmt) {
133     va_list ap;
134 
135     fprintf(stderr, "%s: ", program_name);
136     va_start(ap, fmt);
137     vfprintf(stderr, fmt, ap);
138     va_end(ap);
139 
140     fprintf(stderr, ": %s\n", (const char *)msg);
141   } else {
142     fprintf(stderr, "%s: %s\n", program_name, (const char *)msg);
143   }
144 }
145 
146 void
CtrlUnimplementedCommand(std::string_view command)147 CtrlEngine::CtrlUnimplementedCommand(std::string_view command)
148 {
149   fprintf(stderr, "'%s' command is not implemented\n", command.data());
150   status_code = CTRL_EX_UNIMPLEMENTED;
151 }
152 
153 int
main(int argc,const char ** argv)154 main(int argc, const char **argv)
155 {
156   CtrlEngine engine;
157 
158   engine.parser.add_global_usage("traffic_ctl [OPTIONS] CMD [ARGS ...]");
159   engine.parser.require_commands();
160 
161   engine.parser.add_option("--debug", "", "Enable debugging output")
162     .add_option("--version", "-V", "Print version string")
163     .add_option("--help", "-h", "Print usage information")
164     .add_option("--run-root", "", "using TS_RUNROOT as sandbox", "TS_RUNROOT", 1);
165 
166   auto &alarm_command   = engine.parser.add_command("alarm", "Manipulate alarms").require_commands();
167   auto &config_command  = engine.parser.add_command("config", "Manipulate configuration records").require_commands();
168   auto &metric_command  = engine.parser.add_command("metric", "Manipulate performance metrics").require_commands();
169   auto &server_command  = engine.parser.add_command("server", "Stop, restart and examine the server").require_commands();
170   auto &storage_command = engine.parser.add_command("storage", "Manipulate cache storage").require_commands();
171   auto &plugin_command  = engine.parser.add_command("plugin", "Interact with plugins").require_commands();
172   auto &host_command    = engine.parser.add_command("host", "Interact with host status").require_commands();
173 
174   // alarm commands
175   alarm_command.add_command("clear", "Clear all current alarms", [&]() { engine.alarm_clear(); })
176     .add_example_usage("traffic_ctl alarm clear");
177   alarm_command.add_command("list", "List all current alarms", [&]() { engine.alarm_list(); })
178     .add_example_usage("traffic_ctl alarm list");
179   alarm_command.add_command("resolve", "Resolve the listed alarms", "", MORE_THAN_ONE_ARG_N, [&]() { engine.alarm_resolve(); })
180     .add_example_usage("traffic_ctl alarm resolve ALARM [ALARM ...]");
181 
182   // config commands
183   config_command.add_command("defaults", "Show default information configuration values", [&]() { engine.config_defaults(); })
184     .add_example_usage("traffic_ctl config defaults [OPTIONS]")
185     .add_option("--records", "", "Emit output in records.config format");
186   config_command
187     .add_command("describe", "Show detailed information about configuration values", "", MORE_THAN_ONE_ARG_N,
188                  [&]() { engine.config_describe(); })
189     .add_example_usage("traffic_ctl config describe RECORD [RECORD ...]");
190   config_command.add_command("diff", "Show non-default configuration values", [&]() { engine.config_diff(); })
191     .add_example_usage("traffic_ctl config diff [OPTIONS]")
192     .add_option("--records", "", "Emit output in records.config format");
193   config_command.add_command("get", "Get one or more configuration values", "", MORE_THAN_ONE_ARG_N, [&]() { engine.config_get(); })
194     .add_example_usage("traffic_ctl config get [OPTIONS] RECORD [RECORD ...]")
195     .add_option("--records", "", "Emit output in records.config format");
196   config_command
197     .add_command("match", "Get configuration matching a regular expression", "", MORE_THAN_ONE_ARG_N,
198                  [&]() { engine.config_match(); })
199     .add_example_usage("traffic_ctl config match [OPTIONS] REGEX [REGEX ...]")
200     .add_option("--records", "", "Emit output in records.config format");
201   config_command.add_command("reload", "Request a configuration reload", [&]() { engine.config_reload(); })
202     .add_example_usage("traffic_ctl config reload");
203   config_command.add_command("status", "Check the configuration status", [&]() { engine.config_status(); })
204     .add_example_usage("traffic_ctl config status");
205   config_command.add_command("set", "Set a configuration value", "", 2, [&]() { engine.config_set(); })
206     .add_example_usage("traffic_ctl config set RECORD VALUE");
207 
208   // host commands
209   host_command.add_command("status", "Get one or more host statuses", "", MORE_THAN_ONE_ARG_N, [&]() { engine.status_get(); })
210     .add_example_usage("traffic_ctl host status HOST  [HOST  ...]");
211   host_command.add_command("down", "Set down one or more host(s)", "", MORE_THAN_ONE_ARG_N, [&]() { engine.status_down(); })
212     .add_example_usage("traffic_ctl host down HOST [OPTIONS]")
213     .add_option("--time", "-I", "number of seconds that a host is marked down", "", 1)
214     .add_option("--reason", "", "reason for marking the host down, one of 'manual|active|local", "", 1);
215   host_command.add_command("up", "Set up one or more host(s)", "", MORE_THAN_ONE_ARG_N, [&]() { engine.status_up(); })
216     .add_example_usage("traffic_ctl host up METRIC value")
217     .add_option("--reason", "", "reason for marking the host up, one of 'manual|active|local", "", 1);
218 
219   // metric commands
220   metric_command.add_command("get", "Get one or more metric values", "", MORE_THAN_ONE_ARG_N, [&]() { engine.metric_get(); })
221     .add_example_usage("traffic_ctl metric get METRIC [METRIC ...]");
222   metric_command.add_command("clear", "Clear all metric values", [&]() { engine.metric_clear(); });
223   metric_command.add_command("describe", "Show detailed information about one or more metric values", "", MORE_THAN_ONE_ARG_N,
224                              [&]() { engine.CtrlUnimplementedCommand("describe"); }); // not implemented
225   metric_command.add_command("match", "Get metrics matching a regular expression", "", MORE_THAN_ZERO_ARG_N,
226                              [&]() { engine.metric_match(); });
227   metric_command.add_command("monitor", "Display the value of a metric over time", "", MORE_THAN_ZERO_ARG_N,
228                              [&]() { engine.CtrlUnimplementedCommand("monitor"); }); // not implemented
229   metric_command.add_command("zero", "Clear one or more metric values", "", MORE_THAN_ONE_ARG_N, [&]() { engine.metric_zero(); });
230 
231   // plugin command
232   plugin_command.add_command("msg", "Send message to plugins - a TAG and the message DATA", "", 2, [&]() { engine.plugin_msg(); })
233     .add_example_usage("traffic_ctl plugin msg TAG DATA");
234 
235   // server commands
236   server_command.add_command("backtrace", "Show a full stack trace of the traffic_server process",
237                              [&]() { engine.server_backtrace(); });
238   server_command.add_command("restart", "Restart Traffic Server", [&]() { engine.server_restart(); })
239     .add_example_usage("traffic_ctl server restart [OPTIONS]")
240     .add_option("--drain", "", "Wait for client connections to drain before restarting")
241     .add_option("--manager", "", "Restart traffic_manager as well as traffic_server");
242   server_command.add_command("start", "Start the proxy", [&]() { engine.server_start(); })
243     .add_example_usage("traffic_ctl server start [OPTIONS]")
244     .add_option("--clear-cache", "", "Clear the disk cache on startup")
245     .add_option("--clear-hostdb", "", "Clear the DNS cache on startup");
246   server_command.add_command("status", "Show the proxy status", [&]() { engine.server_status(); })
247     .add_example_usage("traffic_ctl server status");
248   server_command.add_command("stop", "Stop the proxy", [&]() { engine.server_stop(); })
249     .add_example_usage("traffic_ctl server stop [OPTIONS]")
250     .add_option("--drain", "", "Wait for client connections to drain before stopping");
251   server_command.add_command("drain", "Drain the requests", [&]() { engine.server_drain(); })
252     .add_example_usage("traffic_ctl server drain [OPTIONS]")
253     .add_option("--no-new-connection", "-N", "Wait for new connections down to threshold before starting draining")
254     .add_option("--undo", "-U", "Recover server from the drain mode");
255 
256   // storage commands
257   storage_command
258     .add_command("offline", "Take one or more storage volumes offline", "", MORE_THAN_ONE_ARG_N,
259                  [&]() { engine.storage_offline(); })
260     .add_example_usage("storage offline DEVICE [DEVICE ...]");
261   storage_command.add_command("status", "Show the storage configuration", "", MORE_THAN_ZERO_ARG_N,
262                               [&]() { engine.CtrlUnimplementedCommand("status"); }); // not implemented
263 
264   // parse the arguments
265   engine.arguments = engine.parser.parse(argv);
266 
267   BaseLogFile *base_log_file = new BaseLogFile("stderr");
268   diags                      = new Diags("traffic_ctl", "" /* tags */, "" /* actions */, base_log_file);
269 
270   if (engine.arguments.get("debug")) {
271     diags->activate_taglist("traffic_ctl", DiagsTagType_Debug);
272     diags->config.enabled[DiagsTagType_Debug] = true;
273     diags->show_location                      = SHOW_LOCATION_DEBUG;
274   }
275 
276   argparser_runroot_handler(engine.arguments.get("--run-root").value(), argv[0]);
277   Layout::create();
278   RecProcessInit(RECM_STAND_ALONE, diags);
279   LibRecordsConfigInit();
280 
281   ats_scoped_str rundir(RecConfigReadRuntimeDir());
282 
283   // Make a best effort to connect the control socket. If it turns out we are
284   // just displaying help or something then it
285   // doesn't matter that we failed. If we end up performing some operation then
286   // that operation will fail and display the
287   // error.
288   TSInit(rundir, static_cast<TSInitOptionT>(TS_MGMT_OPT_NO_EVENTS | TS_MGMT_OPT_NO_SOCK_TESTS));
289 
290   engine.arguments.invoke();
291 
292   // Done with the mgmt API.
293   TSTerminate();
294 
295   return engine.status_code;
296 }
297