summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/protocol.txt2
-rw-r--r--memcached.c137
-rwxr-xr-xt/stats-conns.t7
3 files changed, 86 insertions, 60 deletions
diff --git a/doc/protocol.txt b/doc/protocol.txt
index b5587a0..962c8dc 100644
--- a/doc/protocol.txt
+++ b/doc/protocol.txt
@@ -1071,6 +1071,8 @@ The following "stat" keywords may be present:
| | sockets this is the listen address. Note that some |
| | socket types (such as UNIX-domain) don't have |
| | meaningful remote addresses. |
+| listen_addr | The address of the server. This field is absent |
+| | for listening sockets. |
| state | The current state of the connection. See below. |
| secs_since_last_cmd | The number of seconds since the most recently |
| | issued command on the connection. This measures |
diff --git a/memcached.c b/memcached.c
index c68781a..2291563 100644
--- a/memcached.c
+++ b/memcached.c
@@ -107,7 +107,8 @@ static int start_conn_timeout_thread();
static void stats_init(void);
static void server_stats(ADD_STAT add_stats, conn *c);
static void process_stat_settings(ADD_STAT add_stats, void *c);
-static void conn_to_str(const conn *c, char *buf);
+static void conn_to_str(const conn *c, char *addr, char *svr_addr);
+
/** Return a datum for stats in binary protocol */
static bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void *c);
@@ -3456,19 +3457,68 @@ static bool get_stats(const char *stat_type, int nkey, ADD_STAT add_stats, void
return ret;
}
-static void conn_to_str(const conn *c, char *buf) {
+static inline void get_conn_text(const conn *c, const int af,
+ char* addr, struct sockaddr *sock_addr) {
char addr_text[MAXPATHLEN];
+ addr_text[0] = '\0';
+ const char *protoname = "?";
+ unsigned short port = 0;
+
+ switch (af) {
+ case AF_INET:
+ (void) inet_ntop(af,
+ &((struct sockaddr_in *)sock_addr)->sin_addr,
+ addr_text,
+ sizeof(addr_text) - 1);
+ port = ntohs(((struct sockaddr_in *)sock_addr)->sin_port);
+ protoname = IS_UDP(c->transport) ? "udp" : "tcp";
+ break;
+
+ case AF_INET6:
+ addr_text[0] = '[';
+ addr_text[1] = '\0';
+ if (inet_ntop(af,
+ &((struct sockaddr_in6 *)sock_addr)->sin6_addr,
+ addr_text + 1,
+ sizeof(addr_text) - 2)) {
+ strcat(addr_text, "]");
+ }
+ port = ntohs(((struct sockaddr_in6 *)sock_addr)->sin6_port);
+ protoname = IS_UDP(c->transport) ? "udp6" : "tcp6";
+ break;
+ case AF_UNIX:
+ strncpy(addr_text,
+ ((struct sockaddr_un *)sock_addr)->sun_path,
+ sizeof(addr_text) - 1);
+ addr_text[sizeof(addr_text)-1] = '\0';
+ protoname = "unix";
+ break;
+ }
+
+ if (strlen(addr_text) < 2) {
+ /* Most likely this is a connected UNIX-domain client which
+ * has no peer socket address, but there's no portable way
+ * to tell for sure.
+ */
+ sprintf(addr_text, "<AF %d>", af);
+ }
+
+ if (port) {
+ sprintf(addr, "%s:%s:%u", protoname, addr_text, port);
+ } else {
+ sprintf(addr, "%s:%s", protoname, addr_text);
+ }
+}
+
+static void conn_to_str(const conn *c, char *addr, char *svr_addr) {
if (!c) {
- strcpy(buf, "<null>");
+ strcpy(addr, "<null>");
} else if (c->state == conn_closed) {
- strcpy(buf, "<closed>");
+ strcpy(addr, "<closed>");
} else {
- const char *protoname = "?";
struct sockaddr_in6 local_addr;
- struct sockaddr *addr = (void *)&c->request_addr;
- int af;
- unsigned short port = 0;
+ struct sockaddr *sock_addr = (void *)&c->request_addr;
/* For listen ports and idle UDP ports, show listen address */
if (c->state == conn_listening ||
@@ -3479,57 +3529,17 @@ static void conn_to_str(const conn *c, char *buf) {
if (getsockname(c->sfd,
(struct sockaddr *)&local_addr,
&local_addr_len) == 0) {
- addr = (struct sockaddr *)&local_addr;
+ sock_addr = (struct sockaddr *)&local_addr;
}
}
+ get_conn_text(c, sock_addr->sa_family, addr, sock_addr);
- af = addr->sa_family;
- addr_text[0] = '\0';
-
- switch (af) {
- case AF_INET:
- (void) inet_ntop(af,
- &((struct sockaddr_in *)addr)->sin_addr,
- addr_text,
- sizeof(addr_text) - 1);
- port = ntohs(((struct sockaddr_in *)addr)->sin_port);
- protoname = IS_UDP(c->transport) ? "udp" : "tcp";
- break;
-
- case AF_INET6:
- addr_text[0] = '[';
- addr_text[1] = '\0';
- if (inet_ntop(af,
- &((struct sockaddr_in6 *)addr)->sin6_addr,
- addr_text + 1,
- sizeof(addr_text) - 2)) {
- strcat(addr_text, "]");
- }
- port = ntohs(((struct sockaddr_in6 *)addr)->sin6_port);
- protoname = IS_UDP(c->transport) ? "udp6" : "tcp6";
- break;
-
- case AF_UNIX:
- strncpy(addr_text,
- ((struct sockaddr_un *)addr)->sun_path,
- sizeof(addr_text) - 1);
- addr_text[sizeof(addr_text)-1] = '\0';
- protoname = "unix";
- break;
- }
-
- if (strlen(addr_text) < 2) {
- /* Most likely this is a connected UNIX-domain client which
- * has no peer socket address, but there's no portable way
- * to tell for sure.
- */
- sprintf(addr_text, "<AF %d>", af);
- }
-
- if (port) {
- sprintf(buf, "%s:%s:%u", protoname, addr_text, port);
- } else {
- sprintf(buf, "%s:%s", protoname, addr_text);
+ if (c->state != conn_listening && !(IS_UDP(c->transport) &&
+ c->state == conn_read)) {
+ struct sockaddr_storage svr_sock_addr;
+ socklen_t svr_addr_len = sizeof(svr_sock_addr);
+ getsockname(c->sfd, (struct sockaddr *)&svr_sock_addr, &svr_addr_len);
+ get_conn_text(c, svr_sock_addr.ss_family, svr_addr, (struct sockaddr *)&svr_sock_addr);
}
}
}
@@ -3538,7 +3548,9 @@ static void process_stats_conns(ADD_STAT add_stats, void *c) {
int i;
char key_str[STAT_KEY_LEN];
char val_str[STAT_VAL_LEN];
- char conn_name[MAXPATHLEN + sizeof("unix:") + sizeof("65535")];
+ size_t extras_len = sizeof("unix:") + sizeof("65535");
+ char addr[MAXPATHLEN + extras_len];
+ char svr_addr[MAXPATHLEN + extras_len];
int klen = 0, vlen = 0;
assert(add_stats);
@@ -3550,10 +3562,17 @@ static void process_stats_conns(ADD_STAT add_stats, void *c) {
* output -- not worth the complexity of the locking that'd be
* required to prevent it.
*/
+ if (IS_UDP(conns[i]->transport)) {
+ APPEND_NUM_STAT(i, "UDP", "%s", "UDP");
+ }
if (conns[i]->state != conn_closed) {
- conn_to_str(conns[i], conn_name);
+ conn_to_str(conns[i], addr, svr_addr);
- APPEND_NUM_STAT(i, "addr", "%s", conn_name);
+ APPEND_NUM_STAT(i, "addr", "%s", addr);
+ if (conns[i]->state != conn_listening &&
+ !(IS_UDP(conns[i]->transport) && conns[i]->state == conn_read)) {
+ APPEND_NUM_STAT(i, "listen_addr", "%s", svr_addr);
+ }
APPEND_NUM_STAT(i, "state", "%s",
state_text(conns[i]->state));
APPEND_NUM_STAT(i, "secs_since_last_cmd", "%d",
diff --git a/t/stats-conns.t b/t/stats-conns.t
index 4dc6554..175ded8 100755
--- a/t/stats-conns.t
+++ b/t/stats-conns.t
@@ -1,7 +1,7 @@
#!/usr/bin/perl
use strict;
-use Test::More tests => 10;
+use Test::More tests => 12;
use FindBin qw($Bin);
use lib "$Bin/lib";
use MemcachedTest;
@@ -44,6 +44,8 @@ like($stats, qr/STAT \d+:state conn_parse_cmd/,
"one client is in command processing");
like($stats, qr/STAT \d+:secs_since_last_cmd [1-9]\r/,
"nonzero secs_since_last_cmd");
+like($stats, qr/STAT \d+:listen_addr unix:\/tmp\/memcachetest\d+\r/,
+ "found listen_addr for the UNIX-domain socket");
$server->stop;
unlink($filename);
@@ -69,3 +71,6 @@ is($1, $server->udpport, "udp port number is correct");
$stats =~ m/STAT \d+:addr tcp:0.0.0.0:(\d+)/;
print STDERR "PORT: ", $server->port, "\n";
is($1, $server->port, "tcp port number is correct");
+
+$stats =~ m/STAT \d+:listen_addr tcp:0.0.0.0:(\d+)/;
+is($1, $server->port, "listen_addr is correct for the tcp port");