blob: da4c9ab181256051146f73ad764e285aa575b821 [file] [log] [blame]
/** @file
A brief file description
@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.
*/
/*****************************************************************************
* Filename: NetworkUtilsRemote.cc
* Purpose: This file contains functions used by remote api client to
* marshal requests to TM and unmarshal replies from TM.
* Also stores the information about the client's current
* socket connection to Traffic Manager
* Created: 8/9/00
*
*
***************************************************************************/
#include "ink_config.h"
#include "ink_defs.h"
#include "ink_sock.h"
#include "ink_string.h"
#include "I_Layout.h"
#include "NetworkUtilsRemote.h"
#include "CoreAPI.h"
#include "CoreAPIShared.h"
#include "EventRegistration.h"
#include "MgmtSocket.h"
extern CallbackTable *remote_event_callbacks;
int main_socket_fd = -1;
int event_socket_fd = -1;
// need to store for reconnecting scenario
char *main_socket_path = NULL; // "<path>/mgmtapisocket"
char *event_socket_path = NULL; // "<path>/eventapisocket"
// From CoreAPIRemote.cc
extern ink_thread ts_test_thread;
extern ink_thread ts_event_thread;
extern TSInitOptionT ts_init_options;
/**********************************************************************
* Socket Helper Functions
**********************************************************************/
void
set_socket_paths(const char *path)
{
// free previously set paths if needed
ats_free(main_socket_path);
ats_free(event_socket_path);
// construct paths based on user input
// form by replacing "mgmtapisocket" with "eventapisocket"
if (path) {
main_socket_path = Layout::relative_to(path, "mgmtapisocket");
event_socket_path = Layout::relative_to(path, "eventapisocket");
} else {
main_socket_path = NULL;
event_socket_path = NULL;
}
return;
}
/**********************************************************************
* socket_test
*
* purpose: performs socket write to check status of other end of connection
* input: None
* output: return 0 if other end of connection closed;
* return < 0 if socket write failed due to some other error
* return > 0 if socket write successful
* notes: send the test msg: UNDEFINED_OP 0(=msg_len)
**********************************************************************/
int
socket_test(int fd)
{
char msg[6]; /* 6 = SIZE_OP + SIZE_LEN */
int16_t op;
int32_t msg_len = 0;
int ret, amount_read = 0;
// write the op
op = (int16_t) UNDEFINED_OP;
memcpy(msg, (void *) &op, SIZE_OP_T);
// write msg-len = 0
memcpy(msg + SIZE_OP_T, &msg_len, SIZE_LEN);
while (amount_read < 6) {
ret = write(fd, msg + amount_read, 6 - amount_read);
if (ret < 0) {
if (errno == EAGAIN)
continue;
if (errno == EPIPE || errno == ENOTCONN) { // other socket end is closed
return 0;
}
return -1;
}
amount_read += ret;
}
return 1; // write was successful; connection still open
}
/***************************************************************************
* connect
*
* purpose: connects to the port on traffic server that listens to mgmt
* requests & issues out responses and alerts
* 1) create and set the client socket_fd; connect to TM
* 2) create and set the client's event_socket_fd; connect to TM
* output: TS_ERR_OKAY - if both sockets sucessfully connect to TM
* TS_ERR_NET_ESTABLISH - at least one unsuccessful connection
* notes: If connection breaks it is responsibility of client to reconnect
* otherwise traffic server will assume mgmt stopped request and
* goes back to just sitting and listening for connection.
***************************************************************************/
TSError
ts_connect()
{
struct sockaddr_un client_sock;
struct sockaddr_un client_event_sock;
int sockaddr_len;
// make sure a socket path is set up
if (!main_socket_path || !event_socket_path)
goto ERROR;
// create a socket
main_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (main_socket_fd < 0) {
//fprintf(stderr, "[connect] ERROR: can't open socket\n");
goto ERROR; // ERROR - can't open socket
}
// setup Unix domain socket
memset(&client_sock, 0, sizeof(sockaddr_un));
client_sock.sun_family = AF_UNIX;
ink_strlcpy(client_sock.sun_path, main_socket_path, sizeof(client_sock.sun_path));
#if defined(darwin) || defined(freebsd)
sockaddr_len = sizeof(sockaddr_un);
#else
sockaddr_len = sizeof(client_sock.sun_family) + strlen(client_sock.sun_path);
#endif
// connect call
if (connect(main_socket_fd, (struct sockaddr *) &client_sock, sockaddr_len) < 0) {
fprintf(stderr, "[connect] ERROR (main_socket_fd %d): %s\n", main_socket_fd, strerror(int(errno)));
close(main_socket_fd);
main_socket_fd = -1;
goto ERROR; //connection is down
}
// -------- set up the event socket ------------------
// create a socket
event_socket_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (event_socket_fd < 0) {
//fprintf(stderr, "[connect] ERROR: can't open event socket\n");
close(main_socket_fd); // close the other socket too!
main_socket_fd = -1;
goto ERROR; // ERROR - can't open socket
}
// setup Unix domain socket
memset(&client_event_sock, 0, sizeof(sockaddr_un));
client_event_sock.sun_family = AF_UNIX;
ink_strlcpy(client_event_sock.sun_path, event_socket_path, sizeof(client_sock.sun_path));
#if defined(darwin) || defined(freebsd)
sockaddr_len = sizeof(sockaddr_un);
#else
sockaddr_len = sizeof(client_event_sock.sun_family) + strlen(client_event_sock.sun_path);
#endif
// connect call
if (connect(event_socket_fd, (struct sockaddr *) &client_event_sock, sockaddr_len) < 0) {
//fprintf(stderr, "[connect] ERROR (event_socket_fd %d): %s\n", event_socket_fd, strerror(int(errno)));
close(event_socket_fd);
close(main_socket_fd);
event_socket_fd = -1;
main_socket_fd = -1;
goto ERROR; //connection is down
}
return TS_ERR_OKAY;
ERROR:
return TS_ERR_NET_ESTABLISH;
}
/***************************************************************************
* disconnect
*
* purpose: disconnect from traffic server; closes sockets and resets their values
* input: None
* output: TS_ERR_FAIL, TS_ERR_OKAY
* notes: doesn't do clean up - all cleanup should be done before here
***************************************************************************/
TSError
disconnect()
{
int ret;
if (main_socket_fd > 0) {
ret = close(main_socket_fd);
main_socket_fd = -1;
if (ret < 0)
return TS_ERR_FAIL;
}
if (event_socket_fd > 0) {
ret = close(event_socket_fd);
event_socket_fd = -1;
if (ret < 0)
return TS_ERR_FAIL;
}
return TS_ERR_OKAY;
}
/***************************************************************************
* reconnect
*
* purpose: reconnects to TM (eg. when TM restarts); does all the necesarry
* set up for reconnection
* input: None
* output: TS_ERR_FAIL, TS_ERR_OKAY
* notes: necessarry events for a new client-TM connection:
* 1) get new socket_fd using old socket_path by calling connect()
* 2) relaunch event_poll_thread_main with new socket_fd
* 3) re-notify TM of all the client's registered callbacks by send msg
***************************************************************************/
TSError
reconnect()
{
TSError err;
err = disconnect();
if (err != TS_ERR_OKAY) // problem disconnecting
return err;
// use the socket_path that was called by remote client on first init
// use connect instead of TSInit() b/c if TM restarted, client-side tables
// would be recreated; just want to reconnect to same socket_path
err = ts_connect();
if (err != TS_ERR_OKAY) // problem establishing connection
return err;
// relaunch a new event thread since socket_fd changed
if (0 == (ts_init_options & TS_MGMT_OPT_NO_EVENTS)) {
ts_event_thread = ink_thread_create(event_poll_thread_main, &event_socket_fd, 0, DEFAULT_STACK_SIZE);
// reregister the callbacks on the TM side for this new client connection
if (remote_event_callbacks) {
err = send_register_all_callbacks(event_socket_fd, remote_event_callbacks);
if (err != TS_ERR_OKAY) // problem establishing connection
return err;
}
} else {
ts_event_thread = static_cast<ink_thread>(NULL);
}
return TS_ERR_OKAY;
}
/***************************************************************************
* reconnect_loop
*
* purpose: attempts to reconnect to TM (eg. when TM restarts) for the
* specified number of times
* input: num_attempts - number of reconnection attempts to try before quit
* output: TS_ERR_OKAY - if successfully reconnected within num_attempts
* TS_ERR_xx - the reason the reconnection failed
* notes:
***************************************************************************/
TSError
reconnect_loop(int num_attempts)
{
int numTries = 0;
TSError err = TS_ERR_FAIL;
while (numTries < num_attempts) {
numTries++;
err = reconnect();
if (err == TS_ERR_OKAY) {
//fprintf(stderr, "[reconnect_loop] Successful reconnction; Leave loop\n");
return TS_ERR_OKAY; // successful connection
}
sleep(1); // to make it slower
}
//fprintf(stderr, "[reconnect_loop] FAIL TO CONNECT after %d tries\n", num_attempts);
return err; // unsuccessful connection after num_attempts
}
/*************************************************************************
* connect_and_send
*
* purpose:
* When sending a request, it's possible that the user had restarted
* Traffic Manager. This means that the connection between TM and
* the remote client has been broken, so the client needs to re-"connect"
* to Traffic Manager. So, after "writing" to the socket in each
* "send_xx_request" function, need to check if the TM socket has
* been closed or not; the "write" function's errno will indicate if
* the other end of the socket has been closed or not. If it is closed,
* then need to try to re"connect", then resend the message request if
* the "connect" was successful.
* 1) try connect()
* 2) if connect() success, then resend the request.
* output: TS_ERR_NET_xx - connection problem or TS_ERR_OKAY
* notes:
* This function is basically called by the special "socket_write_conn" fn
* which will call this fn if it tries to write to the socket and discovers
* the local end of the socket is closed
* Warning: system also sends a SIGPIPE error when try to write to socket
* which is not open; which will by default terminate the process;
* client needs to "ignore" the SIGPIPE signal
**************************************************************************/
TSError
connect_and_send(const char *msg, int msg_len)
{
TSError err;
int total_wrote = 0, ret;
// connects to TM and does all necessary event updates required
err = reconnect();
if (err != TS_ERR_OKAY)
return err;
// makes sure the descriptor is writable
if (socket_write_timeout(main_socket_fd, MAX_TIME_WAIT, 0) <= 0) {
return TS_ERR_NET_TIMEOUT;
}
// connection successfully established; resend msg
// socket_fd should be new fd
while (total_wrote < msg_len) {
ret = write(main_socket_fd, msg + total_wrote, msg_len - total_wrote);
if (ret == 0) {
return TS_ERR_NET_EOF;
}
if (ret < 0) {
if (errno == EAGAIN)
continue;
else if (errno == EPIPE || errno == ENOTCONN) {
// clean-up sockets
close(main_socket_fd);
close(event_socket_fd);
main_socket_fd = -1;
event_socket_fd = -1;
return TS_ERR_NET_ESTABLISH; // can't establish connection
} else
return TS_ERR_NET_WRITE; // general socket writing error
}
total_wrote += ret;
}
return TS_ERR_OKAY;
}
static TSError
socket_read_conn(int fd, uint8_t * buf, size_t needed)
{
size_t consumed = 0;
ssize_t ret;
while (needed > consumed) {
ret = read(fd, buf, needed - consumed);
if (ret < 0) {
if (errno == EAGAIN) {
continue;
} else {
return TS_ERR_NET_READ;
}
}
if (ret == 0) {
return TS_ERR_NET_EOF;
}
buf += ret;
consumed += ret;
}
return TS_ERR_OKAY;
}
/**************************************************************************
* socket_write_conn
*
* purpose: guarantees writing of n bytes; if connection error, tries
* reconnecting to TM again (in case TM was restarted)
* input: fd to write to, buffer to write from & number of bytes to write
* output: TS_ERR_xx
* note: EPIPE - this happens if client makes a call after stopping then
* starting TM again.
* ENOTCONN - this happens if the client tries to make a call after
* stopping TM, but before starting it; then restarts TM and makes a
* new call
* In the send_xx_request function, use a special socket writing function
* which calls connect_and_send() instead of just the basic connect():
* 1) if the write returns EPIPE error, then call connect_and_send()
* 2) return the value returned from EPIPE
*************************************************************************/
static TSError
socket_write_conn(int fd, const char *msg_buf, int bytes)
{
int ret, byte_wrote = 0;
// makes sure the descriptor is writable
if (socket_write_timeout(fd, MAX_TIME_WAIT, 0) <= 0) {
return TS_ERR_NET_TIMEOUT;
}
// write until we fulfill the number
while (byte_wrote < bytes) {
ret = write(fd, msg_buf + byte_wrote, bytes - byte_wrote);
if (ret == 0) {
return TS_ERR_NET_EOF;
}
if (ret < 0) {
if (errno == EAGAIN)
continue;
else if (errno == EPIPE || errno == ENOTCONN) { // other socket end is closed
// clean-up of sockets is done in reconnect()
return connect_and_send(msg_buf, bytes);
} else
return TS_ERR_NET_WRITE;
}
// we are all good here
byte_wrote += ret;
}
return TS_ERR_OKAY;
}
/**********************************************************************
* socket_test_thread
*
* purpose: continually polls to check if local end of socket connection
* is still open; this thread is created when the client calls
* Init() to initialize the API; and will not
* die until the client process dies
* input: none
* output: if other end is closed, it reconnects to TM
* notes: uses the current main_socket_fd because the main_socket_fd could be
* in flux; basically it is possible that the client will reconnect
* from some other call, thus making the main_socket_fd actually
* valid when socket_test is called
* reason: decided to create this "watcher" thread for the socket
* connection because if TM is restarted or the client process
* is started before the TM process, then the client will not
* be able to receive any event notifications until a "request"
* is issued. In order to prevent losing an event notifications
* that are called in between the time TM is restarted and
* client issues a first request, we just run this thread which
* will try to reconnect to TM if it is not already connected
**********************************************************************/
void *
socket_test_thread(void *)
{
// loop until client process dies
while (1) {
if (main_socket_fd == -1 || socket_test(main_socket_fd) <= 0) {
// ASSUMES that in between the time the socket_test is made
// and this reconnect call is made, the main_socket_fd remains
// the same (eg. no one else called reconnect to TM successfully!!
// WHAT IF in between this time, the client had issued a request
// calling socket_write_conn which then calls reconnect(); then
// reconnect will return an "ALREADY CONNECTED" error when it
// tries to connect, and on the next loop iteration, the socket_test
// will actually pass because main_socket_fd is valid!!
if (reconnect() == TS_ERR_OKAY) {
//fprintf(stderr, "[socket_test_thread] reconnect succeeds\n");
}
}
sleep(5);
}
ink_thread_exit(NULL);
return NULL;
}
/**********************************************************************
* MARSHALL REQUESTS
**********************************************************************/
/**********************************************************************
* send_request
*
* purpose: sends file read request to Traffic Manager
* input: fd - file descriptor to use to send to
* op - the type of OpType request sending
* output: TS_ERR_xx
* notes: used by operations which don't need to send any additional
* parameters
* format: <OpType> <msg_len=0>
**********************************************************************/
TSError
send_request(int fd, OpType op)
{
int16_t op_t;
int32_t msg_len;
char msg_buf[SIZE_OP_T + SIZE_LEN];
TSError err;
// fill in op type
op_t = (int16_t) op;
memcpy(msg_buf, &op_t, SIZE_OP_T);
// fill in msg_len == 0
msg_len = 0;
memcpy(msg_buf + SIZE_OP_T, &msg_len, SIZE_LEN);
// send message
err = socket_write_conn(fd, msg_buf, SIZE_OP_T + SIZE_LEN);
return err;
}
/**********************************************************************
* send_request_name (helper fn)
*
* purpose: sends generic request with one string argument name
* input: fd - file descriptor to use
* op - .
* output: TS_ERR_xx
* note: format: <OpType> <str_len> <string>
**********************************************************************/
TSError
send_request_name(int fd, OpType op, const char *name)
{
char *msg_buf;
int16_t op_t;
int32_t msg_len;
int total_len;
TSError err;
if (name == NULL) { //reg callback for all events when op==EVENT_REG_CALLBACK
msg_len = 0;
} else {
msg_len = (int32_t) strlen(name);
}
total_len = SIZE_OP_T + SIZE_LEN + msg_len;
msg_buf = (char *)ats_malloc(sizeof(char) * total_len);
// fill in op type
op_t = (int16_t) op;
memcpy(msg_buf, (void *) &op_t, SIZE_OP_T);
// fill in msg_len
memcpy(msg_buf + SIZE_OP_T, (void *) &msg_len, SIZE_LEN);
// fill in name (if NOT NULL)
if (name)
memcpy(msg_buf + SIZE_OP_T + SIZE_LEN, name, msg_len);
// send message
err = socket_write_conn(fd, msg_buf, total_len);
ats_free(msg_buf);
return err;
}
/**********************************************************************
* send_request_name_value (helper fn)
*
* purpose: sends generic request with 2 str arguments; a name-value pair
* input: fd - file descriptor to use
* op - Op type
* output: TS_ERR_xx
* note: format: <OpType> <name-len> <val-len> <name> <val>
**********************************************************************/
TSError
send_request_name_value(int fd, OpType op, const char *name, const char *value)
{
char *msg_buf;
int msg_pos = 0, total_len;
int32_t msg_len, name_len, val_size; // these are written to msg
int16_t op_t;
TSError err;
if (!name || !value)
return TS_ERR_PARAMS;
// set the sizes
name_len = strlen(name);
val_size = strlen(value);
msg_len = (SIZE_LEN * 2) + name_len + val_size;
total_len = SIZE_OP_T + SIZE_LEN + msg_len;
msg_buf = (char *)ats_malloc(sizeof(char) * (total_len));
// fill in op type
op_t = (int16_t) op;
memcpy(msg_buf + msg_pos, (void *) &op_t, SIZE_OP_T);
msg_pos += SIZE_OP_T;
// fill in msg length
memcpy(msg_buf + msg_pos, (void *) &msg_len, SIZE_LEN);
msg_pos += SIZE_LEN;
// fill in record name length
memcpy(msg_buf + msg_pos, (void *) &name_len, SIZE_LEN);
msg_pos += SIZE_LEN;
// fill in record value length
memcpy(msg_buf + msg_pos, (void *) &val_size, SIZE_LEN);
msg_pos += SIZE_LEN;
// fill in record name
memcpy(msg_buf + msg_pos, name, name_len);
msg_pos += name_len;
// fill in record value
memcpy(msg_buf + msg_pos, value, val_size);
// send message
err = socket_write_conn(fd, msg_buf, total_len);
ats_free(msg_buf);
return err;
}
/**********************************************************************
* send_request_bool (helper)
*
* purpose: sends a simple op with a boolean flag argument
* input: fd - file descriptor to use
* flag - boolean flag
* output: TS_ERR_xx
**********************************************************************/
TSError
send_request_bool(int fd, OpType op, bool flag)
{
char msg_buf[SIZE_OP_T + SIZE_LEN + SIZE_BOOL];
int16_t flag_t;
int32_t msg_len;
// Fill in the operator
memcpy(msg_buf, (void *) &op, SIZE_OP_T);
// fill in msg_len = SIZE_BOOL
msg_len = (int32_t) SIZE_BOOL;
memcpy(msg_buf + SIZE_OP_T, (void *)&msg_len, SIZE_LEN);
// Fill in the argument (the boolean flag)
flag_t = flag ? 1 : 0;
memcpy(msg_buf + SIZE_OP_T + SIZE_LEN, (void *) &flag_t, SIZE_BOOL);
// send message
return socket_write_conn(fd, msg_buf, SIZE_OP_T + SIZE_LEN + SIZE_BOOL);
}
/**********************************************************************
* send_file_read_request
*
* purpose: sends file read request to Traffic Manager
* input: fd - file descriptor to use to send to
* file - file to read
* output: TS_ERR_xx
* notes: first must create the message and then send it across network
* msg format = <OpType> <msg_len> <TSFileNameT>
**********************************************************************/
TSError
send_file_read_request(int fd, TSFileNameT file)
{
char msg_buf[SIZE_OP_T + SIZE_LEN + SIZE_FILE_T];
int msg_pos = 0;
int32_t msg_len = (int32_t) SIZE_FILE_T; //marshalled values
int16_t op, file_t;
// fill in op type
op = (int16_t) FILE_READ;
memcpy(msg_buf + msg_pos, &op, SIZE_OP_T);
msg_pos += SIZE_OP_T;
// fill in msg length
memcpy(msg_buf + msg_pos, &msg_len, SIZE_LEN);
msg_pos += SIZE_LEN;
// fill in file type
file_t = (int16_t) file;
memcpy(msg_buf + msg_pos, &file_t, SIZE_FILE_T);
// send message
return socket_write_conn(fd, msg_buf, SIZE_OP_T + SIZE_LEN + SIZE_FILE_T);
}
/**********************************************************************
* send_file_write_request
*
* purpose: sends file write request to Traffic Manager
* input: fd - file descriptor to use
* file - file to read
* text - new text to write to specified file
* size - length of the text
* ver - version of the file to be written
* output: TS_ERR_xx
* notes: format - FILE_WRITE <msg_len> <file_type> <file_ver> <file_size> <text>
**********************************************************************/
TSError
send_file_write_request(int fd, TSFileNameT file, int ver, int size, char *text)
{
char *msg_buf;
int msg_pos = 0, total_len;
int32_t msg_len, f_size; //marshalled values
int16_t op, file_t, f_ver;
TSError err;
if (!text)
return TS_ERR_PARAMS;
msg_len = SIZE_FILE_T + SIZE_VER + SIZE_LEN + size;
total_len = SIZE_OP_T + SIZE_LEN + msg_len;
msg_buf = (char *)ats_malloc(sizeof(char) * total_len);
// fill in op type
op = (int16_t) FILE_WRITE;
memcpy(msg_buf + msg_pos, &op, SIZE_OP_T);
msg_pos += SIZE_OP_T;
// fill in msg length
memcpy(msg_buf + msg_pos, &msg_len, SIZE_LEN);
msg_pos += SIZE_LEN;
// fill in file type
file_t = (int16_t) file;
memcpy(msg_buf + msg_pos, &file_t, SIZE_FILE_T);
msg_pos += SIZE_FILE_T;
// fill in file version
f_ver = (int16_t) ver;
memcpy(msg_buf + msg_pos, &f_ver, SIZE_VER);
msg_pos += SIZE_VER;
// fill in file size
f_size = (int32_t) size; //typecase to be safe
memcpy(msg_buf + msg_pos, &f_size, SIZE_LEN);
msg_pos += SIZE_LEN;
// fill in text of file
memcpy(msg_buf + msg_pos, text, size);
// send message
err = socket_write_conn(fd, msg_buf, total_len);
ats_free(msg_buf);
return err;
}
static TSError
send_record_get_x_request(OpType optype, int fd, const char *rec_name)
{
char *msg_buf;
int msg_pos = 0, total_len;
int16_t op = (int16_t)optype;
int32_t msg_len;
TSError err;
ink_assert(op == RECORD_GET || op == RECORD_MATCH_GET);
if (!rec_name) {
return TS_ERR_PARAMS;
}
total_len = SIZE_OP_T + SIZE_LEN + strlen(rec_name);
msg_buf = (char *)ats_malloc(sizeof(char) * total_len);
// fill in op type
memcpy(msg_buf + msg_pos, (void *) &op, SIZE_OP_T);
msg_pos += SIZE_OP_T;
// fill in msg length
msg_len = (int32_t) strlen(rec_name);
memcpy(msg_buf + msg_pos, (void *) &msg_len, SIZE_LEN);
msg_pos += SIZE_LEN;
// fill in record name
memcpy(msg_buf + msg_pos, rec_name, strlen(rec_name));
// send message
err = socket_write_conn(fd, msg_buf, total_len);
ats_free(msg_buf);
return err;
}
/**********************************************************************
* send_record_get_request
*
* purpose: sends request to get record value from Traffic Manager
* input: fd - file descriptor to use
* rec_name - name of record to retrieve value for
* output: TS_ERR_xx
* format: RECORD_GET <msg_len> <rec_name>
**********************************************************************/
TSError
send_record_get_request(int fd, const char *rec_name)
{
return send_record_get_x_request(RECORD_GET, fd, rec_name);
}
/**********************************************************************
* send_record_match_request
*
* purpose: sends request to get a list of matching record values from Traffic Manager
* input: fd - file descriptor to use
* rec_name - regex to match against record names
* output: TS_ERR_xx
* format: sequence of RECORD_GET <msg_len> <rec_name>
**********************************************************************/
TSError
send_record_match_request(int fd, const char *rec_regex)
{
return send_record_get_x_request(RECORD_MATCH_GET, fd, rec_regex);
}
/*------ control functions -------------------------------------------*/
/**********************************************************************
* send_proxy_state_get_request
*
* purpose: sends request to get the proxy state (on/off)
* input: fd - file descriptor to use
* output: TS_ERR_xx
* note: format: PROXY_STATE_GET 0(=msg_len)
**********************************************************************/
TSError
send_proxy_state_get_request(int fd)
{
TSError err;
err = send_request(fd, PROXY_STATE_GET);
return err;
}
/**********************************************************************
* send_proxy_state_set_request
*
* purpose: sends request to set the proxy state (on/off)
* input: fd - file descriptor to use
* state - TS_PROXY_ON, TS_PROXY_OFF
* output: TS_ERR_xx
* note: format: PROXY_STATE_SET <msg_len> <TSProxyStateT> <TSCacheClearT>
**********************************************************************/
TSError
send_proxy_state_set_request(int fd, TSProxyStateT state, TSCacheClearT clear)
{
char msg_buf[SIZE_OP_T + SIZE_LEN + SIZE_PROXY_T + SIZE_TS_ARG_T];
int16_t op, state_t, cache_t;
int32_t msg_len;
// fill in op type
op = (int16_t) PROXY_STATE_SET;
memcpy(msg_buf, (void *) &op, SIZE_OP_T);
// fill in msg_len
msg_len = (int32_t) (SIZE_PROXY_T + SIZE_TS_ARG_T);
memcpy(msg_buf + SIZE_OP_T, (void *) &msg_len, SIZE_LEN);
// fill in proxy state
state_t = (int16_t) state;
memcpy(msg_buf + SIZE_OP_T + SIZE_LEN, (void *) &state_t, SIZE_PROXY_T);
// fill in cache clearing option
cache_t = (int16_t) clear;
memcpy(msg_buf + SIZE_OP_T + SIZE_LEN + SIZE_PROXY_T, (void *) &cache_t, SIZE_TS_ARG_T);
// send message
return socket_write_conn(fd, msg_buf, SIZE_OP_T + SIZE_LEN + SIZE_PROXY_T + SIZE_TS_ARG_T);
}
/*------ events -------------------------------------------------------*/
/**********************************************************************
* send_register_all_callbacks
*
* purpose: determines all events which have at least one callback registered
* and sends message to notify TM that this client has a callback
* registered for each event
* input: None
* output: return TS_ERR_OKAY only if ALL events sent okay
* notes: could create a function which just sends a list of all the events to
* reregister; but actually just reuse the function
* send_request_name(EVENT_REG_CALLBACK) and call it for each event
* 1) get list of all events with callbacks
* 2) for each event, call send_request_name
**********************************************************************/
TSError
send_register_all_callbacks(int fd, CallbackTable * cb_table)
{
LLQ *events_with_cb;
TSError err, send_err = TS_ERR_FAIL;
bool no_errors = true; // set to false if one send is not okay
events_with_cb = get_events_with_callbacks(cb_table);
// need to check that the list has all the events registered
if (!events_with_cb) { // all events have registered callback
err = send_request_name(fd, EVENT_REG_CALLBACK, NULL);
if (err != TS_ERR_OKAY)
return err;
} else {
char *event_name;
int event_id;
int num_events = queue_len(events_with_cb);
// iterate through the LLQ and send request for each event
for (int i = 0; i < num_events; i++) {
event_id = *(int *) dequeue(events_with_cb);
event_name = (char *) get_event_name(event_id);
if (event_name) {
err = send_request_name(fd, EVENT_REG_CALLBACK, event_name);
ats_free(event_name); // free memory
if (err != TS_ERR_OKAY) {
send_err = err; // save the type of send error
no_errors = false;
}
}
// REMEMBER: WON"T GET A REPLY from TM side!
}
}
if (events_with_cb)
delete_queue(events_with_cb);
if (no_errors)
return TS_ERR_OKAY;
else
return send_err;
}
/**********************************************************************
* send_unregister_all_callbacks
*
* purpose: determines all events which have no callback registered
* and sends message to notify TM that this client has no
* callbacks registered for that event
* input: None
* output: TS_ERR_OKAY only if all send requests are okay
* notes: could create a function which just sends a list of all the events to
* unregister; but actually just reuse the function
* send_request_name(EVENT_UNREG_CALLBACK) and call it for each event
**********************************************************************/
TSError
send_unregister_all_callbacks(int fd, CallbackTable * cb_table)
{
char *event_name;
int event_id;
LLQ *events_with_cb; // list of events with at least one callback
int reg_callback[NUM_EVENTS];
TSError err, send_err = TS_ERR_FAIL;
bool no_errors = true; // set to false if at least one send fails
// init array so that all events don't have any callbacks
for (int i = 0; i < NUM_EVENTS; i++) {
reg_callback[i] = 0;
}
events_with_cb = get_events_with_callbacks(cb_table);
if (!events_with_cb) { // all events have a registered callback
return TS_ERR_OKAY;
} else {
int num_events = queue_len(events_with_cb);
// iterate through the LLQ and mark events that have a callback
for (int i = 0; i < num_events; i++) {
event_id = *(int *) dequeue(events_with_cb);
reg_callback[event_id] = 1; // mark the event as having a callback
}
delete_queue(events_with_cb);
}
// send message to TM to mark unregister
for (int k = 0; k < NUM_EVENTS; k++) {
if (reg_callback[k] == 0) { // event has no registered callbacks
event_name = get_event_name(k);
err = send_request_name(fd, EVENT_UNREG_CALLBACK, event_name);
ats_free(event_name);
if (err != TS_ERR_OKAY) {
send_err = err; //save the type of the sending error
no_errors = false;
}
// REMEMBER: WON"T GET A REPLY!
// only the event_poll_thread_main does any reading of the event_socket;
// so DO NOT parse reply b/c a reply won't be sent
}
}
if (no_errors)
return TS_ERR_OKAY;
else
return send_err;
}
/**********************************************************************
* send_diags_msg
*
* purpose: sends the diag msg across along with they diag msg type
* input: mode - type of diags msg
* msg - the diags msg
* output: TS_ERR_xx
* note: format: <OpType> <msg_len> <TSDiagsT> <diag_msg_len> <diag_msg>
**********************************************************************/
TSError
send_diags_msg(int fd, TSDiagsT mode, const char *diag_msg)
{
char *msg_buf;
int16_t op_t, diag_t;
int32_t msg_len, diag_msg_len;
int total_len;
TSError err;
if (!diag_msg)
return TS_ERR_PARAMS;
diag_msg_len = (int32_t) strlen(diag_msg);
msg_len = SIZE_DIAGS_T + SIZE_LEN + diag_msg_len;
total_len = SIZE_OP_T + SIZE_LEN + msg_len;
msg_buf = (char *)ats_malloc(sizeof(char) * total_len);
// fill in op type
op_t = (int16_t) DIAGS;
memcpy(msg_buf, (void *) &op_t, SIZE_OP_T);
// fill in entire msg len
memcpy(msg_buf + SIZE_OP_T, (void *) &msg_len, SIZE_LEN);
// fill in TSDiagsT
diag_t = (int16_t) mode;
memcpy(msg_buf + SIZE_OP_T + SIZE_LEN, (void *) &diag_t, SIZE_DIAGS_T);
// fill in diags msg_len
memcpy(msg_buf + SIZE_OP_T + SIZE_LEN + SIZE_DIAGS_T, (void *) &diag_msg_len, SIZE_LEN);
// fill in diags msg
memcpy(msg_buf + SIZE_OP_T + SIZE_LEN + SIZE_DIAGS_T + SIZE_LEN, diag_msg, diag_msg_len);
// send message
err = socket_write_conn(fd, msg_buf, total_len);
ats_free(msg_buf);
return err;
}
/**********************************************************************
* UNMARSHAL REPLIES
**********************************************************************/
/* Error handling implementation:
* All the parsing functions which parse the reply returned from local side
* also must read the TSERror return value sent from local side; this return
* value is the same value that will be returned by the parsing function.
* ALL PARSING FUNCTIONS MUST FIRST CHECK that the retval is TS_ERR_OKAY;
* if it is not, then DON"T PARSE THE REST OF THE REPLY!!
*/
/* Reading replies:
* The reading is done in while loop in the parse_xx_reply functions;
* need to add a timeout so that the function is not left looping and
* waiting if a msg isn't sent to the socket from local side (eg. TM died)
*/
/**********************************************************************
* parse_reply
*
* purpose: parses a reply from traffic manager. return that error
* input: fd
* output: errors on error or fill up class with response &
* return TS_ERR_xx
* notes: only returns an TSError
**********************************************************************/
TSError
parse_reply(int fd)
{
TSError ret;
int16_t ret_val;
// check to see if anything to read; wait for specified time = 1 sec
if (socket_read_timeout(fd, MAX_TIME_WAIT, 0) <= 0) { // time expires before ready to read
return TS_ERR_NET_TIMEOUT;
}
ret = socket_read_conn(fd, (uint8_t *)&ret_val, SIZE_ERR_T);
if (ret != TS_ERR_OKAY) {
return ret;
}
return (TSError) ret_val;
}
/**********************************************************************
* parse_reply_list
*
* purpose: parses a TM reply to a request to get a list of string tokens
* input: fd - socket to read
* list - will contain delimited string list of tokens
* output: TS_ERR_xx
* notes:
* format: <TSError> <string_list_len> <delimited_string_list>
**********************************************************************/
TSError
parse_reply_list(int fd, char **list)
{
int16_t ret_val;
int32_t list_size;
TSError err_t;
if (!list) {
return TS_ERR_PARAMS;
}
// check to see if anything to read; wait for specified time
if (socket_read_timeout(fd, MAX_TIME_WAIT, 0) <= 0) {
return TS_ERR_NET_TIMEOUT;
}
// get the return value (TSError type)
err_t = socket_read_conn(fd, (uint8_t *)&ret_val, SIZE_ERR_T);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// if !TS_ERR_OKAY, stop reading rest of msg
err_t = (TSError) ret_val;
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// now get size of string event list
err_t = socket_read_conn(fd, (uint8_t *)&list_size, SIZE_LEN);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// get the delimited event list string
*list = (char *)ats_malloc(sizeof(char) * (list_size + 1));
err_t = socket_read_conn(fd, (uint8_t *)(*list), list_size);
if (err_t != TS_ERR_OKAY) {
ats_free(*list);
*list = NULL;
return err_t;
}
// add end of string to end of the record value
((char *) (*list))[list_size] = '\0';
return err_t;
}
/**********************************************************************
* parse_file_read_reply
*
* purpose: parses a file read reply from traffic manager.
* input: fd
* ver -
* size - size of text
* text -
* output: errors on error or fill up class with response &
* return TS_ERR_xx
* notes: reply format = <TSError> <file_version> <file_size> <text>
**********************************************************************/
TSError
parse_file_read_reply(int fd, int *ver, int *size, char **text)
{
int32_t f_size;
int16_t ret_val, f_ver;
TSError err_t;
if (!ver || !size || !text)
return TS_ERR_PARAMS;
// check to see if anything to read; wait for specified time
if (socket_read_timeout(fd, MAX_TIME_WAIT, 0) <= 0) { // time expires before ready to read
return TS_ERR_NET_TIMEOUT;
}
// get the error return value
err_t = socket_read_conn(fd, (uint8_t *)&ret_val, SIZE_ERR_T);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// if !TS_ERR_OKAY, stop reading rest of msg
err_t = (TSError) ret_val;
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// now get file version
err_t = socket_read_conn(fd, (uint8_t *)&f_ver, SIZE_VER);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
*ver = (int) f_ver;
// now get file size
err_t = socket_read_conn(fd, (uint8_t *)&f_size, SIZE_LEN);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
*size = (int) f_size;
// check size before reading text
if ((*size) <= 0) {
*text = ats_strndup("", 1); // set to empty string
return TS_ERR_OKAY;
}
// now we got the size, we can read everything into our msg * then parse it
*text = (char *)ats_malloc(sizeof(char) * (f_size + 1));
err_t = socket_read_conn(fd, (uint8_t *)(*text), f_size);
if (err_t != TS_ERR_OKAY) {
ats_free(*text);
*text = NULL;
return err_t;
}
(*text)[f_size] = '\0'; // end the string
return TS_ERR_OKAY;
}
/**********************************************************************
* parse_record_get_reply
*
* purpose: parses a record_get reply from traffic manager.
* input: fd
* retval -
* rec_type - the type of the record
* rec_value - the value of the record in string format
* output: errors on error or fill up class with response &
* return SUCC
* notes: reply format = <TSError> <val_size> <name_size> <rec_type> <record_value> <record_name>
* Zero-length values and names are supported. If the size field is 0, the corresponding
* value field is not transmitted.
* It's the responsibility of the calling function to conver the rec_value
* based on the rec_type!!
**********************************************************************/
TSError
parse_record_get_reply(int fd, TSRecordT * rec_type, void **rec_val, char **rec_name)
{
int16_t ret_val, rec_t;
int32_t val_size, name_size;
TSError err_t;
if (!rec_type || !rec_val) {
return TS_ERR_PARAMS;
}
*rec_name = NULL;
*rec_val = NULL;
// check to see if anything to read; wait for specified time
if (socket_read_timeout(fd, MAX_TIME_WAIT, 0) <= 0) { //time expired before ready to read
return TS_ERR_NET_TIMEOUT;
}
// get the return value (TSError type)
err_t = socket_read_conn(fd, (uint8_t *)&ret_val, SIZE_ERR_T);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
// if !TS_ERR_OKAY, stop reading rest of msg
err_t = (TSError) ret_val;
if (err_t != TS_ERR_OKAY) {
goto fail;
}
// now get size of record_value
err_t = socket_read_conn(fd, (uint8_t *)&val_size, SIZE_LEN);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
// now get size of record name
err_t = socket_read_conn(fd, (uint8_t *)&name_size, SIZE_LEN);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
// get the record type
err_t = socket_read_conn(fd, (uint8_t *)&rec_t, SIZE_REC_T);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
*rec_type = (TSRecordT) rec_t;
// get record value (if there is one)
if (val_size) {
if (*rec_type == TS_REC_STRING) {
*rec_val = ats_malloc(sizeof(char) * (val_size + 1));
} else {
*rec_val = ats_malloc(sizeof(char) * (val_size));
}
err_t = socket_read_conn(fd, (uint8_t *)(*rec_val), val_size);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
// add end of string to end of the record value
if (*rec_type == TS_REC_STRING) {
((char *) (*rec_val))[val_size] = '\0';
}
}
// get the record name (if there is one)
if (name_size) {
*rec_name = (char *)ats_malloc(sizeof(char) * (name_size + 1));
err_t = socket_read_conn(fd, (uint8_t *)(*rec_name), name_size);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
(*rec_name)[name_size] = '\0';
}
return TS_ERR_OKAY;
fail:
ats_free(*rec_val);
ats_free(*rec_name);
*rec_val = NULL;
*rec_name = NULL;
return err_t;
}
/**********************************************************************
* parse_record_set_reply
*
* purpose: parses a record_set reply from traffic manager.
* input: fd
* action_need - will contain the type of action needed from the set
* output: TS_ERR_xx
* notes: reply format = <TSError> <val_size> <rec_type> <record_value>
* It's the responsibility of the calling function to conver the rec_value
* based on the rec_type!!
**********************************************************************/
TSError
parse_record_set_reply(int fd, TSActionNeedT * action_need)
{
int16_t ret_val, action_t;
TSError err_t;
if (!action_need)
return TS_ERR_PARAMS;
// check to see if anything to read; wait for specified time
if (socket_read_timeout(fd, MAX_TIME_WAIT, 0) <= 0) {
return TS_ERR_NET_TIMEOUT;
}
// get the return value (TSError type)
err_t = socket_read_conn(fd, (uint8_t *)&ret_val, SIZE_ERR_T);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// if !TS_ERR_OKAY, stop reading rest of msg
err_t = (TSError) ret_val;
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// now get the action needed
err_t = socket_read_conn(fd, (uint8_t *)&action_t, SIZE_ACTION_T);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
*action_need = (TSActionNeedT) action_t;
return err_t;
}
/**********************************************************************
* parse_proxy_state_get_reply
*
* purpose: parses a TM reply to a PROXY_STATE_GET request
* input: fd
* state - will contain the state of the proxy
* output: TS_ERR_xx
* notes: function is DIFFERENT becuase it has NO TSError at head of msg
* format: <TSProxyStateT>
**********************************************************************/
TSError
parse_proxy_state_get_reply(int fd, TSProxyStateT * state)
{
TSError err_t;
int16_t state_t;
if (!state)
return TS_ERR_PARAMS;
// check to see if anything to read; wait for specified time
if (socket_read_timeout(fd, MAX_TIME_WAIT, 0) <= 0) { // time expires before ready to read
return TS_ERR_NET_TIMEOUT;
}
// now get proxy state
err_t = socket_read_conn(fd, (uint8_t *)&state_t, SIZE_PROXY_T);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
*state = (TSProxyStateT) state_t;
return TS_ERR_OKAY;
}
/*------------- events ---------------------------------------------*/
/**********************************************************************
* parse_event_active_reply
*
* purpose: parses a TM reply to a request to get status of an event
* input: fd - socket to read
* is_active - set to true if event is active; false otherwise
* output: TS_ERR_xx
* notes:
* format: reply format = <TSError> <bool>
**********************************************************************/
TSError
parse_event_active_reply(int fd, bool * is_active)
{
int16_t ret_val, active;
TSError err_t;
if (!is_active)
return TS_ERR_PARAMS;
// check to see if anything to read; wait for specified time
if (socket_read_timeout(fd, MAX_TIME_WAIT, 0) <= 0) {
return TS_ERR_NET_TIMEOUT;
}
// get the return value (TSError type)
err_t = socket_read_conn(fd, (uint8_t *)&ret_val, SIZE_ERR_T);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// if !TS_ERR_OKAY, stop reading rest of msg
err_t = (TSError) ret_val;
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// now get the boolean
err_t = socket_read_conn(fd, (uint8_t *)&active, SIZE_BOOL);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
*is_active = (bool) active;
return err_t;
}
/**********************************************************************
* parse_event_notification
*
* purpose: parses the event notification message from TM when an event
* is signalled; stores the event info in the TSEvent
* input: fd - socket to read
* event - where the event info from msg is stored
* output:TS_ERR_OKAY, TS_ERR_NET_READ, TS_ERR_NET_EOF, TS_ERR_PARAMS
* notes:
* format: <OpType> <event_name_len> <event_name> <desc_len> <desc>
**********************************************************************/
TSError
parse_event_notification(int fd, TSEvent * event)
{
OpType msg_type;
int16_t type_op;
int32_t msg_len;
char *event_name = NULL, *desc = NULL;
TSError err_t;
if (!event)
return TS_ERR_PARAMS;
// read the operation type; should be EVENT_NOTIFY
err_t = socket_read_conn(fd, (uint8_t *)&type_op, SIZE_OP_T);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// got the message type; the msg_type should be EVENT_NOTIFY
msg_type = (OpType) type_op;
if (msg_type != EVENT_NOTIFY) {
return TS_ERR_FAIL;
}
// read in event name length
err_t = socket_read_conn(fd, (uint8_t *)&msg_len, SIZE_LEN);
if (err_t != TS_ERR_OKAY) {
return err_t;
}
// read the event name
event_name = (char *)ats_malloc(sizeof(char) * (msg_len + 1));
err_t = socket_read_conn(fd, (uint8_t *)event_name, msg_len);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
event_name[msg_len] = '\0'; // end the string
// read in event description length
err_t = socket_read_conn(fd, (uint8_t *)&msg_len, SIZE_LEN);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
// read the event description
desc = (char *)ats_malloc(sizeof(char) * (msg_len + 1));
err_t = socket_read_conn(fd, (uint8_t *)desc, msg_len);
if (err_t != TS_ERR_OKAY) {
goto fail;
}
desc[msg_len] = '\0'; // end the string
// fill in event info
event->name = event_name;
event->id = (int) get_event_id(event_name);
event->description = desc;
return TS_ERR_OKAY;
fail:
ats_free(event_name);
ats_free(desc);
return err_t;
}