blob: 010fc7bca61dad56ee71df2be4e5fb8c62a14b21 [file] [log] [blame]
/** @file
Some utility and support functions for the management module.
@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 "tscore/ink_platform.h"
#include "tscore/ink_sock.h"
#include "MgmtUtils.h"
#include "MgmtSocket.h"
#include "tscore/Diags.h"
#include "LocalManager.h"
static int use_syslog = 0;
/* mgmt_use_syslog()
*
* Called to indicate that the syslog should be used and
* the log has been opened
*/
void
mgmt_use_syslog()
{
use_syslog = 1;
}
/*
* mgmt_readline(...)
* Simple, inefficient, read line function. Takes a socket to read
* from, a char * to write into, and a max len to read. The newline
* is stripped.
*
* Returns: num bytes read
* -1 error
*/
int
mgmt_readline(int soc, char *buf, int maxlen)
{
int n = 0;
char c;
for (; n < maxlen; n++) {
int rc = read_socket(soc, &c, 1);
if (rc == 1) {
*buf++ = c;
if (c == '\n') {
--buf;
*buf = '\0';
if (*(buf - 1) == '\r') {
--buf;
*buf = '\0';
}
break;
}
} else if (rc == 0) {
return n;
} else { /* Error */
if (errno == ECONNRESET || errno == EPIPE) {
return n ? n : 0;
}
if (mgmt_transient_error()) {
mgmt_sleep_msec(1);
continue;
}
return -1;
}
}
return n;
} /* End mgmt_readline */
/*
* mgmt_writeline(...)
* Simple, inefficient, write line function. Takes a soc to write to,
* a char * containing the data, and the number of bytes to write.
* It sends nbytes + 1 bytes worth of data, the + 1 being the newline
* character.
*
* Returns: num bytes not written
* -1 error
*/
int
mgmt_writeline(int soc, const char *data, int nbytes)
{
int nleft, n = 0;
const char *tmp = data;
nleft = nbytes;
while (nleft > 0) {
int nwritten = write_socket(soc, tmp, nleft);
if (nwritten == 0) { // Nothing written
mgmt_sleep_msec(1);
continue;
} else if (nwritten < 0) { // Error
if (mgmt_transient_error()) {
mgmt_sleep_msec(1);
continue;
}
return nwritten;
}
nleft -= nwritten;
tmp += nwritten;
}
while (n != 1) {
n = write_socket(soc, "\n", 1); /* Terminating newline */
if (n == 0) {
mgmt_sleep_msec(1);
continue;
} else if (n < 0) { // Error
if (mgmt_transient_error()) {
mgmt_sleep_msec(1);
continue;
}
return n;
}
}
return (nleft); /* Paranoia */
} /* End mgmt_writeline */
/*
* mgmt_read_pipe()
* - Reads from a pipe
*
* Returns: bytes read
* 0 on EOF
* -errno on error
*/
int
mgmt_read_pipe(int fd, char *buf, int bytes_to_read)
{
char *p = buf;
int bytes_read = 0;
while (bytes_to_read > 0) {
int err = read_socket(fd, p, bytes_to_read);
if (err == 0) {
// return 0 if partial read.
return err;
} else if (err < 0) {
// Turn ECONNRESET into EOF.
if (errno == ECONNRESET || errno == EPIPE) {
return bytes_read ? bytes_read : 0;
}
if (mgmt_transient_error()) {
mgmt_sleep_msec(1);
continue;
}
return -errno;
}
bytes_to_read -= err;
bytes_read += err;
p += err;
}
return bytes_read;
}
/*
* mgmt_write_pipe()
* - Writes to a pipe
*
* Returns: bytes written
* 0 on EOF
* -errno on error
*/
int
mgmt_write_pipe(int fd, char *buf, int bytes_to_write)
{
char *p = buf;
int bytes_written = 0;
while (bytes_to_write > 0) {
int err = write_socket(fd, p, bytes_to_write);
if (err == 0) {
// Where this volume of IEEE Std 1003.1-2001 requires -1 to be returned and errno set to [EAGAIN],
// most historical implementations return zero for write(2)
mgmt_sleep_msec(1);
continue;
} else if (err < 0) {
if (mgmt_transient_error()) {
mgmt_sleep_msec(1);
continue;
}
return -errno;
}
bytes_to_write -= err;
bytes_written += err;
p += err;
}
return bytes_written;
}
void
mgmt_log(const char *message_format, ...)
{
va_list ap;
char extended_format[4096], message[4096];
va_start(ap, message_format);
if (diags) {
NoteV(message_format, ap);
} else {
if (use_syslog) {
snprintf(extended_format, sizeof(extended_format), "log ==> %s", message_format);
vsprintf(message, extended_format, ap);
syslog(LOG_WARNING, "%s", message);
} else {
snprintf(extended_format, sizeof(extended_format), "[E. Mgmt] log ==> %s", message_format);
vsprintf(message, extended_format, ap);
ink_assert(fwrite(message, strlen(message), 1, stderr) == 1);
}
}
va_end(ap);
return;
} /* End mgmt_log */
void
mgmt_elog(const int lerrno, const char *message_format, ...)
{
va_list ap;
char extended_format[4096], message[4096];
va_start(ap, message_format);
if (diags) {
ErrorV(message_format, ap);
if (lerrno != 0) {
Error("last system error %d: %s", lerrno, strerror(lerrno));
}
} else {
if (use_syslog) {
snprintf(extended_format, sizeof(extended_format), "ERROR ==> %s", message_format);
vsprintf(message, extended_format, ap);
syslog(LOG_ERR, "%s", message);
if (lerrno != 0) {
syslog(LOG_ERR, " (last system error %d: %s)", lerrno, strerror(lerrno));
}
} else {
snprintf(extended_format, sizeof(extended_format), "Manager ERROR: %s", message_format);
vsprintf(message, extended_format, ap);
ink_assert(fwrite(message, strlen(message), 1, stderr) == 1);
if (lerrno != 0) {
snprintf(message, sizeof(message), "(last system error %d: %s)", lerrno, strerror(lerrno));
ink_assert(fwrite(message, strlen(message), 1, stderr) == 1);
}
}
}
va_end(ap);
return;
} /* End mgmt_elog */
void
mgmt_fatal(const int lerrno, const char *message_format, ...)
{
va_list ap;
va_start(ap, message_format);
if (diags) {
if (lerrno != 0) {
Error("last system error %d: %s", lerrno, strerror(lerrno));
}
FatalV(message_format, ap);
} else {
char extended_format[4096], message[4096];
snprintf(extended_format, sizeof(extended_format), "FATAL ==> %s", message_format);
vsprintf(message, extended_format, ap);
ink_assert(fwrite(message, strlen(message), 1, stderr) == 1);
if (use_syslog) {
syslog(LOG_ERR, "%s", message);
}
if (lerrno != 0) {
fprintf(stderr, "[E. Mgmt] last system error %d: %s", lerrno, strerror(lerrno));
if (use_syslog) {
syslog(LOG_ERR, " (last system error %d: %s)", lerrno, strerror(lerrno));
}
}
}
va_end(ap);
mgmt_cleanup();
::exit(1);
} /* End mgmt_fatal */
static inline int
get_interface_mtu(int sock_fd, struct ifreq *ifr)
{
if (ioctl(sock_fd, SIOCGIFMTU, ifr) < 0) {
mgmt_log("[getAddrForIntr] Unable to obtain MTU for "
"interface '%s'",
ifr->ifr_name);
return 0;
} else {
#if defined(solaris) || defined(hpux)
return ifr->ifr_metric;
#else
return ifr->ifr_mtu;
#endif
}
}
bool
mgmt_getAddrForIntr(char *intrName, sockaddr *addr, int *mtu)
{
bool found = false;
if (intrName == nullptr) {
return false;
}
int fakeSocket; // a temporary socket to pass to ioctl
struct ifconf ifc; // ifconf information
char *ifbuf; // ifconf buffer
struct ifreq *ifr, *ifend; // pointer to individual interface info
int lastlen;
int len;
// Prevent UMRs
memset(addr, 0, sizeof(struct in_addr));
if ((fakeSocket = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
mgmt_fatal(errno, "[getAddrForIntr] Unable to create socket\n");
}
// INKqa06739
// Fetch the list of network interfaces
// . from Stevens, Unix Network Prog., pg 434-435
ifbuf = nullptr;
lastlen = 0;
len = 128 * sizeof(struct ifreq); // initial buffer size guess
for (;;) {
ifbuf = static_cast<char *>(ats_malloc(len));
memset(ifbuf, 0, len); // prevent UMRs
ifc.ifc_len = len;
ifc.ifc_buf = ifbuf;
if (ioctl(fakeSocket, SIOCGIFCONF, &ifc) < 0) {
if (errno != EINVAL || lastlen != 0) {
mgmt_fatal(errno, "[getAddrForIntr] Unable to read network interface configuration\n");
}
} else {
if (ifc.ifc_len == lastlen) {
break;
}
lastlen = ifc.ifc_len;
}
len *= 2;
ats_free(ifbuf);
}
found = false;
// Loop through the list of interfaces
ifend = reinterpret_cast<struct ifreq *>(ifc.ifc_buf + ifc.ifc_len);
for (ifr = ifc.ifc_req; ifr < ifend;) {
if (ifr->ifr_addr.sa_family == AF_INET && strcmp(ifr->ifr_name, intrName) == 0) {
// Get the address of the interface
if (ioctl(fakeSocket, SIOCGIFADDR, reinterpret_cast<char *>(ifr)) < 0) {
mgmt_log("[getAddrForIntr] Unable obtain address for network interface %s\n", intrName);
} else {
// Only look at the address if it an internet address
if (ifr->ifr_ifru.ifru_addr.sa_family == AF_INET) {
ats_ip_copy(addr, &ifr->ifr_ifru.ifru_addr);
found = true;
if (mtu) {
*mtu = get_interface_mtu(fakeSocket, ifr);
}
break;
} else {
mgmt_log("[getAddrForIntr] Interface %s is not configured for IP.\n", intrName);
}
}
}
#if defined(freebsd) || defined(darwin)
ifr = (struct ifreq *)((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
#else
ifr = reinterpret_cast<struct ifreq *>((reinterpret_cast<char *>(ifr)) + sizeof(*ifr));
#endif
}
ats_free(ifbuf);
close(fakeSocket);
return found;
} /* End mgmt_getAddrForIntr */
/*
* mgmt_sortipaddrs(...)
* Routine to sort and pick smallest ip addr.
*/
struct in_addr *
mgmt_sortipaddrs(int num, struct in_addr **list)
{
int i = 0;
unsigned long min;
struct in_addr *entry, *tmp;
min = (list[0])->s_addr;
entry = list[0];
while (i < num && (tmp = list[i]) != nullptr) {
i++;
if (min > tmp->s_addr) {
min = tmp->s_addr;
entry = tmp;
}
}
return entry;
} /* End mgmt_sortipaddrs */
void
mgmt_sleep_sec(int seconds)
{
sleep(seconds);
}
void
mgmt_sleep_msec(int msec)
{
usleep(msec * 1000);
}