diff --git a/cmd/traffic_ctl/Makefile.am b/cmd/traffic_ctl/Makefile.am index 5b7731bc764..df7d26d454f 100644 --- a/cmd/traffic_ctl/Makefile.am +++ b/cmd/traffic_ctl/Makefile.am @@ -29,6 +29,7 @@ traffic_ctl_SOURCES = \ alarm.cc \ config.cc \ metric.cc \ + plugin.cc \ server.cc \ storage.cc \ traffic_ctl.cc diff --git a/cmd/traffic_ctl/plugin.cc b/cmd/traffic_ctl/plugin.cc new file mode 100644 index 00000000000..9ae2dd02439 --- /dev/null +++ b/cmd/traffic_ctl/plugin.cc @@ -0,0 +1,52 @@ +/** @file + + Plugin related sub commands. + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "traffic_ctl.h" + +static int +plugin_msg(unsigned argc, const char **argv) +{ + if (!CtrlProcessArguments(argc, argv, NULL, 0) || n_file_arguments != 2) { + return CtrlCommandUsage("plugin msg TAG DATA"); + } + + TSMgmtError error; + + error = TSLifecycleMessage(file_arguments[0], file_arguments[1], strlen(file_arguments[1]) + 1); + if (error != TS_ERR_OKAY) { + CtrlMgmtError(error, "message '%s' not sent", file_arguments[0]); + return CTRL_EX_ERROR; + } + + return CTRL_EX_OK; +} + +int +subcommand_plugin(unsigned argc, const char **argv) +{ + const subcommand commands[] = { + {plugin_msg, "msg", "Send message to plugins - a TAG and the message DATA"}, + }; + + return CtrlGenericSubcommand("plugin", commands, countof(commands), argc, argv); +} diff --git a/cmd/traffic_ctl/traffic_ctl.cc b/cmd/traffic_ctl/traffic_ctl.cc index eb2698c5c33..5e905f8202c 100644 --- a/cmd/traffic_ctl/traffic_ctl.cc +++ b/cmd/traffic_ctl/traffic_ctl.cc @@ -226,6 +226,7 @@ main(int argc, const char **argv) {subcommand_metric, "metric", "Manipulate performance metrics"}, {subcommand_server, "server", "Stop, restart and examine the server"}, {subcommand_storage, "storage", "Manipulate cache storage"}, + {subcommand_plugin, "plugin", "Interact with plugins"}, }; BaseLogFile *base_log_file = new BaseLogFile("stderr"); diff --git a/cmd/traffic_ctl/traffic_ctl.h b/cmd/traffic_ctl/traffic_ctl.h index daa61c1b409..80c662ea145 100644 --- a/cmd/traffic_ctl/traffic_ctl.h +++ b/cmd/traffic_ctl/traffic_ctl.h @@ -201,6 +201,7 @@ int subcommand_config(unsigned argc, const char **argv); int subcommand_metric(unsigned argc, const char **argv); int subcommand_server(unsigned argc, const char **argv); int subcommand_storage(unsigned argc, const char **argv); +int subcommand_plugin(unsigned argc, const char **argv); // Exit status codes, following BSD's sysexits(3) #define CTRL_EX_OK 0 diff --git a/doc/appendices/command-line/traffic_ctl.en.rst b/doc/appendices/command-line/traffic_ctl.en.rst index 6d32561e7d8..5f79f15a844 100644 --- a/doc/appendices/command-line/traffic_ctl.en.rst +++ b/doc/appendices/command-line/traffic_ctl.en.rst @@ -48,6 +48,8 @@ of subcommands that control different aspects of Traffic Server: Stop, restart and examine the server :program:`traffic_ctl storage` Manipulate cache storage +:program:`traffic_ctl plugin` + Interact with plugins. Options ======= @@ -254,6 +256,15 @@ traffic_ctl storage that storage. This does not persist across restarts of the :program:`traffic_server` process. +traffic_ctl plugin +------------------- +.. program:: traffic_ctl plugin +.. option:: msg TAG DATA + + Send a message to plugins. All plugins that have hooked the :c:member:`TS_LIFECYCLE_MSG_HOOK` + will receive a callback for that hook. The :arg:`TAG` and :arg:`DATA` will be available to the + plugin hook processing. + Examples ======== diff --git a/doc/developer-guide/api/functions/TSLifecycleHookAdd.en.rst b/doc/developer-guide/api/functions/TSLifecycleHookAdd.en.rst index 6674c6d4153..5499bc02ed9 100644 --- a/doc/developer-guide/api/functions/TSLifecycleHookAdd.en.rst +++ b/doc/developer-guide/api/functions/TSLifecycleHookAdd.en.rst @@ -73,6 +73,15 @@ is always called before another unless specifically mentioned. Invoked with the event :c:data:`TS_EVENT_LIFECYCLE_CACHE_READY` and ``NULL`` data. +.. c:var:: TS_LIFECYCLE_MSG_HOOK + + Called when triggered by an external process, such as :program:`traffic_ctl`. + + Invoked with the event :c:data:`TS_EVENT_LIFECYCLE_MSG`. The data is an instance of the + :c:type:`TSPluginMsg`. This contains a *tag* which is a null terminated string and a data payload. + The payload cannot be assumed to be null terminated and is created by the external agent. + + Ordering ======== diff --git a/doc/developer-guide/api/types/TSLifecycleHookID.en.rst b/doc/developer-guide/api/types/TSLifecycleHookID.en.rst index 96d5d16386e..e9b5546bce7 100644 --- a/doc/developer-guide/api/types/TSLifecycleHookID.en.rst +++ b/doc/developer-guide/api/types/TSLifecycleHookID.en.rst @@ -41,6 +41,8 @@ Enumeration Members .. c:member:: TSLifecycleHookID TS_LIFECYCLE_SERVER_SSL_CTX_INITIALIZED_HOOK .. c:member:: TSLifecycleHookID TS_LIFECYCLE_CLIENT_SSL_CTX_INITIALIZED_HOOK + +.. c:member:: TSLifecycleHookID MSG_HOOK .. c:member:: TSLifecycleHookID TS_LIFECYCLE_LAST_HOOK diff --git a/example/lifecycle-plugin/lifecycle-plugin.c b/example/lifecycle-plugin/lifecycle-plugin.c index 5cda67b1433..a8f8fe2af34 100644 --- a/example/lifecycle-plugin/lifecycle-plugin.c +++ b/example/lifecycle-plugin/lifecycle-plugin.c @@ -27,13 +27,13 @@ #include #include +#include #include int -CallbackHandler(TSCont this, TSEvent id, void *no_data) +CallbackHandler(TSCont this, TSEvent id, void *data) { - (void)this; - (void)no_data; + (void)this; // make compiler shut up about unused variable. switch (id) { case TS_EVENT_LIFECYCLE_PORTS_INITIALIZED: TSDebug("lifecycle-plugin", "Proxy ports initialized"); @@ -44,6 +44,11 @@ CallbackHandler(TSCont this, TSEvent id, void *no_data) case TS_EVENT_LIFECYCLE_CACHE_READY: TSDebug("lifecycle-plugin", "Cache ready"); break; + case TS_EVENT_LIFECYCLE_MSG: { + TSPluginMsg *msg = (TSPluginMsg *)data; + TSDebug("lifecycle-plugin", "Message to '%s' - %" PRIu64 " bytes of data", msg->tag, msg->data_size); + break; + } default: TSDebug("lifecycle-plugin", "Unexpected event %d", id); break; @@ -105,6 +110,7 @@ TSPluginInit(int argc, const char *argv[]) TSLifecycleHookAdd(TS_LIFECYCLE_PORTS_INITIALIZED_HOOK, cb); TSLifecycleHookAdd(TS_LIFECYCLE_PORTS_READY_HOOK, cb); TSLifecycleHookAdd(TS_LIFECYCLE_CACHE_READY_HOOK, cb); + TSLifecycleHookAdd(TS_LIFECYCLE_MSG_HOOK, cb); TSDebug("lifecycle-plugin", "online"); diff --git a/lib/ts/apidefs.h.in b/lib/ts/apidefs.h.in index 77c101e94b6..89fdfc48f6e 100644 --- a/lib/ts/apidefs.h.in +++ b/lib/ts/apidefs.h.in @@ -110,6 +110,12 @@ typedef struct { char *support_email; } TSPluginRegistrationInfo; +typedef struct { + char const* tag; ///< Message tag (null terminated). + void const* data; ///< Message data (payload) + size_t data_size; ///< Amount of message data. +} TSPluginMsg; + /** This set of enums are possible values returned by TSHttpHdrParseReq() and TSHttpHdrParseResp(). @@ -319,6 +325,12 @@ typedef enum { Event: TS_EVENT_LIFECYCLE_CACHE_READY + TS_LIFECYCLE_MSG_HOOK + + Called in response to an external agent. The data is a pointer to an instance of TSPluginMsg. + + Event: TS_EVENT_LIFECYCLE_MSG + TS_LIFECYCLE_SERVER_SSL_CTX_INITIALIZED_HOOK called every time after a server SSL_CTX has finished the initialization. @@ -346,6 +358,7 @@ typedef enum { TS_LIFECYCLE_CACHE_READY_HOOK, TS_LIFECYCLE_SERVER_SSL_CTX_INITIALIZED_HOOK, TS_LIFECYCLE_CLIENT_SSL_CTX_INITIALIZED_HOOK, + TS_LIFECYCLE_MSG_HOOK, TS_LIFECYCLE_LAST_HOOK } TSLifecycleHookID; @@ -440,6 +453,7 @@ typedef enum { TS_EVENT_LIFECYCLE_SERVER_SSL_CTX_INITIALIZED = 60021, TS_EVENT_LIFECYCLE_CLIENT_SSL_CTX_INITIALIZED = 60022, TS_EVENT_VCONN_PRE_ACCEPT = 60023, + TS_EVENT_LIFECYCLE_MSG = 60024, TS_EVENT_MGMT_UPDATE = 60100, /* EVENTS 60200 - 60202 for internal use */ diff --git a/mgmt/BaseManager.h b/mgmt/BaseManager.h index c816f0cb071..37f8c6b80a2 100644 --- a/mgmt/BaseManager.h +++ b/mgmt/BaseManager.h @@ -70,6 +70,7 @@ // so it's easier to do this than to try to encode an opcode and yet another // case statement. #define MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE 10011 +#define MGMT_EVENT_LIFECYCLE_MESSAGE 10012 /*********************************************************************** * diff --git a/mgmt/ProcessManager.cc b/mgmt/ProcessManager.cc index 87f76f99486..5b09050dfd0 100644 --- a/mgmt/ProcessManager.cc +++ b/mgmt/ProcessManager.cc @@ -316,6 +316,9 @@ ProcessManager::handleMgmtMsgFromLM(MgmtMessageHdr *mh) case MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE: signalMgmtEntity(MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE, data_raw, mh->data_len); break; + case MGMT_EVENT_LIFECYCLE_MESSAGE: + signalMgmtEntity(MGMT_EVENT_LIFECYCLE_MESSAGE, data_raw, mh->data_len); + break; default: mgmt_elog(stderr, 0, "[ProcessManager::pollLMConnection] unknown type %d\n", mh->msg_id); break; diff --git a/mgmt/api/CoreAPI.cc b/mgmt/api/CoreAPI.cc index 605e42cfb0a..6f777d19bec 100644 --- a/mgmt/api/CoreAPI.cc +++ b/mgmt/api/CoreAPI.cc @@ -500,6 +500,18 @@ StorageDeviceCmdOffline(const char *dev) lmgmt->signalEvent(MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE, dev); return TS_ERR_OKAY; } +/*------------------------------------------------------------------------- + * Lifecycle Message + *------------------------------------------------------------------------- + * Signal plugins. + */ +TSMgmtError +LifecycleMessage(char const *tag, void const *data, size_t data_size) +{ + ink_release_assert(!"Not expected to reach here"); + lmgmt->signalEvent(MGMT_EVENT_LIFECYCLE_MESSAGE, tag); + return TS_ERR_OKAY; +} /************************************************************************** * RECORD OPERATIONS *************************************************************************/ diff --git a/mgmt/api/CoreAPI.h b/mgmt/api/CoreAPI.h index e27c212c5bc..390bf59927c 100644 --- a/mgmt/api/CoreAPI.h +++ b/mgmt/api/CoreAPI.h @@ -45,10 +45,11 @@ TSProxyStateT ProxyStateGet(); TSMgmtError ProxyStateSet(TSProxyStateT state, TSCacheClearT clear); TSMgmtError ServerBacktrace(unsigned options, char **trace); -TSMgmtError Reconfigure(); // TS reread config files -TSMgmtError Restart(unsigned options); // restart TM -TSMgmtError Bounce(unsigned options); // restart traffic_server -TSMgmtError StorageDeviceCmdOffline(const char *dev); // Storage device operation. +TSMgmtError Reconfigure(); // TS reread config files +TSMgmtError Restart(unsigned options); // restart TM +TSMgmtError Bounce(unsigned options); // restart traffic_server +TSMgmtError StorageDeviceCmdOffline(const char *dev); // Storage device operation. +TSMgmtError LifecycleMessage(char const *tag, void const *data, size_t data_size); // Lifecycle alert to plugins. /*************************************************************************** * Record Operations diff --git a/mgmt/api/CoreAPIRemote.cc b/mgmt/api/CoreAPIRemote.cc index ee1c931c82a..b934ee80c15 100644 --- a/mgmt/api/CoreAPIRemote.cc +++ b/mgmt/api/CoreAPIRemote.cc @@ -459,6 +459,23 @@ StorageDeviceCmdOffline(char const *dev) return (ret == TS_ERR_OKAY) ? parse_generic_response(STORAGE_DEVICE_CMD_OFFLINE, main_socket_fd) : ret; } +/*------------------------------------------------------------------------- + * LIfecycle Alert + *------------------------------------------------------------------------- + * Send alert to plugins + */ +TSMgmtError +LifecycleMessage(char const *tag, void const *data, size_t data_size) +{ + TSMgmtError ret; + MgmtMarshallInt optype = LIFECYCLE_MESSAGE; + MgmtMarshallString mtag = const_cast(tag); + MgmtMarshallData mdata = {const_cast(data), data_size}; + + ret = MGMTAPI_SEND_MESSAGE(main_socket_fd, LIFECYCLE_MESSAGE, &optype, &mtag, &mdata); + return (ret == TS_ERR_OKAY) ? parse_generic_response(LIFECYCLE_MESSAGE, main_socket_fd) : ret; +} + /*************************************************************************** * Record Operations ***************************************************************************/ diff --git a/mgmt/api/EventControlMain.cc b/mgmt/api/EventControlMain.cc index 44ad69cc5c5..daf879fea13 100644 --- a/mgmt/api/EventControlMain.cc +++ b/mgmt/api/EventControlMain.cc @@ -544,6 +544,7 @@ static const event_message_handler handlers[] = { NULL, // STATS_RESET_CLUSTER NULL, // STORAGE_DEVICE_CMD_OFFLINE NULL, // RECORD_MATCH_GET + NULL, // LIFECYCLE_MESSAGE }; static TSMgmtError diff --git a/mgmt/api/INKMgmtAPI.cc b/mgmt/api/INKMgmtAPI.cc index 596b85ea29d..5fb6809028b 100644 --- a/mgmt/api/INKMgmtAPI.cc +++ b/mgmt/api/INKMgmtAPI.cc @@ -1778,6 +1778,12 @@ TSStorageDeviceCmdOffline(char const *dev) return StorageDeviceCmdOffline(dev); } +tsapi TSMgmtError +TSLifecycleMessage(char const *tag, void const *data, size_t data_size) +{ + return LifecycleMessage(tag, data, data_size); +} + /*--- diags output operations ---------------------------------------------*/ tsapi void TSDiags(TSDiagsT mode, const char *fmt, ...) diff --git a/mgmt/api/NetworkMessage.cc b/mgmt/api/NetworkMessage.cc index 2ef23df2330..de49abef3d3 100644 --- a/mgmt/api/NetworkMessage.cc +++ b/mgmt/api/NetworkMessage.cc @@ -67,6 +67,7 @@ static const struct NetCmdOperation requests[] = { /* API_PING */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}}, /* SERVER_BACKTRACE */ {2, {MGMT_MARSHALL_INT, MGMT_MARSHALL_INT}}, /* RECORD_DESCRIBE_CONFIG */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_INT}}, + /* LIFECYCLE_MESSAGE */ {3, {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA}}, }; // Responses always begin with a TSMgmtError code, followed by additional fields. @@ -108,6 +109,7 @@ static const struct NetCmdOperation responses[] = { MGMT_MARSHALL_INT /* order */, MGMT_MARSHALL_INT /* access */, MGMT_MARSHALL_INT /* update */, MGMT_MARSHALL_INT /* updatetype */, MGMT_MARSHALL_INT /* checktype */, MGMT_MARSHALL_INT /* source */, MGMT_MARSHALL_STRING /* checkexpr */}}, + /* LIFECYCLE_MESSAGE */ {1, {MGMT_MARSHALL_INT}}, }; #define GETCMD(ops, optype, cmd) \ @@ -231,6 +233,7 @@ send_mgmt_error(int fd, OpType optype, TSMgmtError error) return send_mgmt_response(fd, optype, &ecode, &strval); case FILE_READ: + case LIFECYCLE_MESSAGE: ink_release_assert(responses[optype].nfields == 3); return send_mgmt_response(fd, optype, &ecode, &intval, &dataval); diff --git a/mgmt/api/NetworkMessage.h b/mgmt/api/NetworkMessage.h index 88ef72d18ea..7795fbb74fb 100644 --- a/mgmt/api/NetworkMessage.h +++ b/mgmt/api/NetworkMessage.h @@ -60,6 +60,7 @@ typedef enum { API_PING, SERVER_BACKTRACE, RECORD_DESCRIBE_CONFIG, + LIFECYCLE_MESSAGE, UNDEFINED_OP /* This must be last */ } OpType; diff --git a/mgmt/api/TSControlMain.cc b/mgmt/api/TSControlMain.cc index 85060586fef..f9f41f91dd0 100644 --- a/mgmt/api/TSControlMain.cc +++ b/mgmt/api/TSControlMain.cc @@ -1150,6 +1150,29 @@ handle_record_describe(int fd, void *req, size_t reqlen) ats_free(name); return ret; } +/************************************************************************** + * handle_lifecycle_message + * + * purpose: handle lifecyle message to plugins + * output: TS_ERR_xx + * note: None + *************************************************************************/ +static TSMgmtError +handle_lifecycle_message(int fd, void *req, size_t reqlen) +{ + MgmtMarshallInt optype; + MgmtMarshallInt err; + MgmtMarshallString tag; + MgmtMarshallData data; + + err = recv_mgmt_request(req, reqlen, LIFECYCLE_MESSAGE, &optype, &tag, &data); + if (err == TS_ERR_OKAY) { + lmgmt->signalEvent(MGMT_EVENT_LIFECYCLE_MESSAGE, static_cast(req), reqlen); + } + + return send_mgmt_response(fd, LIFECYCLE_MESSAGE, &err); +} +/**************************************************************************/ struct control_message_handler { unsigned flags; @@ -1183,7 +1206,9 @@ static const control_message_handler handlers[] = { /* RECORD_MATCH_GET */ {0, handle_record_match}, /* API_PING */ {0, handle_api_ping}, /* SERVER_BACKTRACE */ {MGMT_API_PRIVILEGED, handle_server_backtrace}, - /* RECORD_DESCRIBE_CONFIG */ {0, handle_record_describe}}; + /* RECORD_DESCRIBE_CONFIG */ {0, handle_record_describe}, + /* LIFECYCLE_MESSAGE */ {MGMT_API_PRIVILEGED, handle_lifecycle_message}, +}; // This should use countof(), but we need a constexpr :-/ #define NUM_OP_HANDLERS (sizeof(handlers) / sizeof(handlers[0])) diff --git a/mgmt/api/include/mgmtapi.h b/mgmt/api/include/mgmtapi.h index 64e4945ca42..ead1feef56d 100644 --- a/mgmt/api/include/mgmtapi.h +++ b/mgmt/api/include/mgmtapi.h @@ -30,6 +30,7 @@ #include #include +#include /*************************************************************************** * System Specific Items @@ -945,6 +946,12 @@ tsapi TSMgmtError TSBounce(unsigned options); */ tsapi TSMgmtError TSStorageDeviceCmdOffline(char const *dev); +/* TSLifecycleMessage: Send a lifecycle message to the plugins. + * @arg tag Alert tag string (null-terminated) + * @return Success + */ +tsapi TSMgmtError TSLifecycleMessage(char const *tag, void const *data, size_t data_size); + /*--- diags output operations ---------------------------------------------*/ /* TSDiags: enables users to manipulate run-time diagnostics, and print * user-formatted notices, warnings and errors diff --git a/proxy/Main.cc b/proxy/Main.cc index e99d41d8e24..890bea147a5 100644 --- a/proxy/Main.cc +++ b/proxy/Main.cc @@ -122,6 +122,7 @@ static const long MAX_LOGIN = ink_login_name_max(); static void *mgmt_restart_shutdown_callback(void *, char *, int data_len); static void *mgmt_storage_device_cmd_callback(void *x, char *data, int len); +static void *mgmt_lifecycle_msg_callback(void *x, char *data, int len); static void init_ssl_ctx_callback(void *ctx, bool server); static void load_ssl_file_callback(const char *ssl_file, unsigned int options); @@ -1905,6 +1906,7 @@ main(int /* argc ATS_UNUSED */, const char **argv) // just to be safe because the value is a #define, not a typed value. pmgmt->registerMgmtCallback(MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE, mgmt_storage_device_cmd_callback, reinterpret_cast(static_cast(MGMT_EVENT_STORAGE_DEVICE_CMD_OFFLINE))); + pmgmt->registerMgmtCallback(MGMT_EVENT_LIFECYCLE_MESSAGE, mgmt_lifecycle_msg_callback, NULL); // The main thread also becomes a net thread. ink_set_thread_name("[ET_NET 0]"); @@ -1972,6 +1974,31 @@ mgmt_storage_device_cmd_callback(void *data, char *arg, int len) return NULL; } +static void * +mgmt_lifecycle_msg_callback(void *, char *data, int len) +{ + APIHook *hook = lifecycle_hooks->get(TS_LIFECYCLE_MSG_HOOK); + TSPluginMsg msg; + MgmtInt op; + MgmtMarshallString tag; + MgmtMarshallData payload; + static const MgmtMarshallType fields[] = {MGMT_MARSHALL_INT, MGMT_MARSHALL_STRING, MGMT_MARSHALL_DATA}; + + if (mgmt_message_parse(data, len, fields, countof(fields), &op, &tag, &payload) == -1) { + Error("[mgmt_lifecyle_msg_callback] RPC message parsing error."); + } else { + msg.tag = tag; + msg.data = payload.ptr; + msg.data_size = payload.len; + while (hook) { + TSPluginMsg tmp(msg); // Just to make sure plugins don't mess this up for others. + hook->invoke(TS_EVENT_LIFECYCLE_MSG, &tmp); + hook = hook->next(); + } + } + return NULL; +} + static void init_ssl_ctx_callback(void *ctx, bool server) {