Skip to content

Commit

Permalink
Merge branch 'lsfs--sockdiag-RW' of https://github.com/masatake/util-…
Browse files Browse the repository at this point in the history
…linux

* 'lsfs--sockdiag-RW' of https://github.com/masatake/util-linux:
  lsfd: (man) update the description of ENDPOINTS column of UNIX-Stream sockets
  tests: (lsfd) extend the mkfds-socketpair case to test ENDPOINTS with SOCK.SHUTDOWN info
  lsfd: append SOCK.SHUTDOWN value to ENDPOINTS column of UNIX-STREAM sockets
  tests: (lsfd) add a case for testing SOCK.SHUTDOWN column
  tests: (test_mkfds::socketpair) add "halfclose" parameter
  lsfd: don't capitalize the help strings for the columns
  lsfd: (man) write about SOCK.SHUTDOWN column
  lsfd: add SOCK.SHUTDOWN column
  lsfd: call xinfo backend method before calling socket generic method when filling columns
  tests: (lsfd) add a case for testing ENDPOINTS column of UNIX-STREAM sockets
  tests: (lsfd::mkfds-socketpair) make a case for testing DGRAM a subtest and add a subtest for STREAM
  lsfd: (man) document the ENDPOINT column for UNIX socket
  lsfd: fill ENDPOINTS column of unix socket using UNIX_DIAG_PEER information
  lsfd: make the sock_xinfo layer be able to prepare an ipc_class for a given socket
  lsfd: initialize the ipc table before loading lists of unix socket peers via netlink diag
  • Loading branch information
karelzak committed Oct 9, 2023
2 parents f2458cf + d58cd0f commit 36cce3d
Show file tree
Hide file tree
Showing 13 changed files with 328 additions and 15 deletions.
142 changes: 140 additions & 2 deletions misc-utils/lsfd-sock-xinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -393,11 +393,21 @@ static void send_diag_request(int diagsd, void *req, size_t req_size,
/*
* UNIX
*/
struct unix_ipc {
struct ipc ipc;
ino_t inode;
ino_t ipeer;
};

struct unix_xinfo {
struct sock_xinfo sock;
int acceptcon; /* flags */
uint16_t type;
uint8_t st;
#define is_shutdown_mask_set(mask) ((mask) & (1 << 2))
#define set_shutdown_mask(mask) ((mask) |= (1 << 2))
uint8_t shutdown_mask:3;
struct unix_ipc *unix_ipc;
char path[
UNIX_PATH_MAX
+ 1 /* for @ */
Expand Down Expand Up @@ -475,15 +485,98 @@ static bool unix_get_listening(struct sock_xinfo *sock_xinfo,
return ux->acceptcon;
}

static unsigned int unix_get_hash(struct file *file)
{
return (unsigned int)(file->stat.st_ino % UINT_MAX);
}

static bool unix_is_suitable_ipc(struct ipc *ipc, struct file *file)
{
return ((struct unix_ipc *)ipc)->inode == file->stat.st_ino;
}

/* For looking up an ipc struct for a sock inode, we need a sock strcuct
* for the inode. See the signature o get_ipc().
*
* However, there is a case that we have no sock strcuct for the inode;
* in the context we know only the sock inode.
* For the case, unix_make_dumy_sock() provides the way to make a
* dummy sock struct for the inode.
*/
static void unix_make_dumy_sock(struct sock *original, ino_t ino, struct sock *dummy)
{
*dummy = *original;
dummy->file.stat.st_ino = ino;
}

static struct ipc_class unix_ipc_class = {
.size = sizeof(struct unix_ipc),
.get_hash = unix_get_hash,
.is_suitable_ipc = unix_is_suitable_ipc,
.free = NULL,
};

static struct ipc_class *unix_get_ipc_class(struct sock_xinfo *sock_xinfo __attribute__((__unused__)),
struct sock *sock __attribute__((__unused__)))
{
return &unix_ipc_class;
}

static bool unix_shutdown_chars(struct unix_xinfo *ux, char rw[2])
{
uint8_t mask = ux->shutdown_mask;

if (is_shutdown_mask_set(mask)) {
rw[0] = ((mask & (1 << 0))? '-': 'r');
rw[1] = ((mask & (1 << 1))? '-': 'w');
return true;
}

return false;
}

static inline char *unix_xstrendpoint(struct sock *sock)
{
char *str = NULL;
char shutdown_chars[3] = { 0 };

if (!unix_shutdown_chars(((struct unix_xinfo *)sock->xinfo), shutdown_chars)) {
shutdown_chars[0] = '?';
shutdown_chars[1] = '?';
}
xasprintf(&str, "%d,%s,%d%c%c",
sock->file.proc->pid, sock->file.proc->command, sock->file.association,
shutdown_chars[0], shutdown_chars[1]);

return str;
}

static struct ipc *unix_get_peer_ipc(struct unix_xinfo *ux,
struct sock *sock)
{
struct unix_ipc *unix_ipc;
struct sock dummy_peer_sock;

unix_ipc = ux->unix_ipc;
if (!unix_ipc)
return NULL;

unix_make_dumy_sock(sock, unix_ipc->ipeer, &dummy_peer_sock);
return get_ipc(&dummy_peer_sock.file);
}

static bool unix_fill_column(struct proc *proc __attribute__((__unused__)),
struct sock_xinfo *sock_xinfo,
struct sock *sock __attribute__((__unused__)),
struct sock *sock,
struct libscols_line *ln __attribute__((__unused__)),
int column_id,
size_t column_index __attribute__((__unused__)),
char **str)
{
struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
struct ipc *peer_ipc;
struct list_head *e;
char shutdown_chars[3] = { 0 };

switch (column_id) {
case COL_UNIX_PATH:
Expand All @@ -492,6 +585,30 @@ static bool unix_fill_column(struct proc *proc __attribute__((__unused__)),
return true;
}
break;
case COL_ENDPOINTS:
peer_ipc = unix_get_peer_ipc(ux, sock);
if (!peer_ipc)
break;

list_for_each_backwardly(e, &peer_ipc->endpoints) {
struct sock *peer_sock = list_entry(e, struct sock, endpoint.endpoints);
char *estr;

if (*str)
xstrputc(str, '\n');
estr = unix_xstrendpoint(peer_sock);
xstrappend(str, estr);
free(estr);
}
if (*str)
return true;
break;
case COL_SOCK_SHUTDOWN:
if (unix_shutdown_chars(ux, shutdown_chars)) {
*str = xstrdup(shutdown_chars);
return true;
}
break;
}

return false;
Expand All @@ -503,6 +620,7 @@ static const struct sock_xinfo_class unix_xinfo_class = {
.get_state = unix_get_state,
.get_listening = unix_get_listening,
.fill_column = unix_fill_column,
.get_ipc_class = unix_get_ipc_class,
.free = NULL,
};

Expand Down Expand Up @@ -588,6 +706,7 @@ static bool handle_diag_unix(ino_t netns __attribute__((__unused__)),
size_t rta_len;
ino_t inode;
struct sock_xinfo *xinfo;
struct unix_xinfo *unix_xinfo;

if (diag->udiag_family != AF_UNIX)
return false;
Expand All @@ -605,6 +724,7 @@ static bool handle_diag_unix(ino_t netns __attribute__((__unused__)),

if (xinfo->class != &unix_xinfo_class)
return true;
unix_xinfo = (struct unix_xinfo *)xinfo;

rta_len = nlmsg_len - NLMSG_LENGTH(sizeof(*diag));
for (struct rtattr *attr = (struct rtattr *)(diag + 1);
Expand All @@ -616,6 +736,24 @@ static bool handle_diag_unix(ino_t netns __attribute__((__unused__)),
case UNIX_DIAG_NAME:
unix_refill_name(xinfo, RTA_DATA(attr), len);
break;

case UNIX_DIAG_SHUTDOWN:
if (len < 1)
break;

unix_xinfo->shutdown_mask = *(uint8_t *)RTA_DATA(attr);
set_shutdown_mask(unix_xinfo->shutdown_mask);
break;

case UNIX_DIAG_PEER:
if (len < 4)
break;

unix_xinfo->unix_ipc = (struct unix_ipc *)new_ipc(&unix_ipc_class);
unix_xinfo->unix_ipc->inode = inode;
unix_xinfo->unix_ipc->ipeer = (ino_t)(*(uint32_t *)RTA_DATA(attr));
add_ipc(&unix_xinfo->unix_ipc->ipc, inode % UINT_MAX);
break;
}
}
return true;
Expand All @@ -626,7 +764,7 @@ static void load_xinfo_from_diag_unix(int diagsd, ino_t netns)
struct unix_diag_req udr = {
.sdiag_family = AF_UNIX,
.udiag_states = -1, /* set the all bits. */
.udiag_show = UDIAG_SHOW_NAME,
.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UNIX_DIAG_SHUTDOWN,
};

send_diag_request(diagsd, &udr, sizeof(udr), handle_diag_unix, netns);
Expand Down
41 changes: 33 additions & 8 deletions misc-utils/lsfd-sock.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,23 @@
static void attach_sock_xinfo(struct file *file)
{
struct sock *sock = (struct sock *)file;

sock->xinfo = get_sock_xinfo(file->stat.st_ino);
if (sock->xinfo) {
struct ipc *ipc = get_ipc(file);
if (ipc)
add_endpoint(&sock->endpoint, ipc);
}
}

static const struct ipc_class *sock_get_ipc_class(struct file *file)
{
struct sock *sock = (struct sock *)file;

if (sock->xinfo && sock->xinfo->class->get_ipc_class)
return sock->xinfo->class->get_ipc_class(sock->xinfo, sock);

return NULL;
}

static bool sock_fill_column(struct proc *proc __attribute__((__unused__)),
Expand All @@ -39,6 +55,15 @@ static bool sock_fill_column(struct proc *proc __attribute__((__unused__)),
{
char *str = NULL;
struct sock *sock = (struct sock *)file;

if (sock->xinfo && sock->xinfo->class
&& sock->xinfo->class->fill_column) {
if (sock->xinfo->class->fill_column(proc, sock->xinfo, sock, ln,
column_id, column_index,
&str))
goto out;
}

switch(column_id) {
case COL_TYPE:
if (!sock->protoname)
Expand Down Expand Up @@ -95,17 +120,14 @@ static bool sock_fill_column(struct proc *proc __attribute__((__unused__)),
? "1"
: "0");
break;
case COL_SOCK_SHUTDOWN:
str = xstrdup("??");
break;
default:
if (sock->xinfo && sock->xinfo->class
&& sock->xinfo->class->fill_column) {
if (sock->xinfo->class->fill_column(proc, sock->xinfo, sock, ln,
column_id, column_index,
&str))
break;
}
return false;
}

out:
if (!str)
err(EXIT_FAILURE, _("failed to add output data"));
if (scols_line_refer_data(ln, column_index, str))
Expand All @@ -116,13 +138,13 @@ static bool sock_fill_column(struct proc *proc __attribute__((__unused__)),
static void init_sock_content(struct file *file)
{
int fd;
struct sock *sock = (struct sock *)file;

assert(file);

fd = file->association;

if (fd >= 0 || fd == -ASSOC_MEM || fd == -ASSOC_SHM) {
struct sock *sock = (struct sock *)file;
char path[PATH_MAX] = {'\0'};
char buf[256];
ssize_t len;
Expand All @@ -143,6 +165,8 @@ static void init_sock_content(struct file *file)
sock->protoname = xstrdup(buf);
}
}

init_endpoint(&sock->endpoint);
}

static void free_sock_content(struct file *file)
Expand Down Expand Up @@ -173,4 +197,5 @@ const struct file_class sock_class = {
.free_content = free_sock_content,
.initialize_class = initialize_sock_class,
.finalize_class = finalize_sock_class,
.get_ipc_class = sock_get_ipc_class,
};
2 changes: 2 additions & 0 deletions misc-utils/lsfd-sock.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct sock {
struct file file;
char *protoname;
struct sock_xinfo *xinfo;
struct ipc_endpoint endpoint;
};

struct sock_xinfo_class {
Expand All @@ -60,6 +61,7 @@ struct sock_xinfo_class {
int,
size_t,
char **str);
struct ipc_class *(*get_ipc_class)(struct sock_xinfo *, struct sock *);

void (*free)(struct sock_xinfo *);
};
Expand Down
19 changes: 19 additions & 0 deletions misc-utils/lsfd.1.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,12 @@ write mode of the endpoint.
eventfd type:::
_PID_,_COMMAND_,_ASSOC_

UNIX-STREAM:::
_PID_,_COMMAND_,_ASSOC_[-r?][-w?]
+
About the last characters ([-r?][-w?]), see the description
of _SOCK.SHUTDOWN_.

EVENTFD.ID <``number``>::
Eventfd ID.

Expand Down Expand Up @@ -420,6 +426,19 @@ Inode identifying network namespace where the socket belongs to.
SOCK.PROTONAME <``string``>::
Protocol name.

SOCK.SHUTDOWN <``string``>::
Shutdown state of socket.
+
[-r?]:::
If the first character is _r_, the receptions are allowed.
If it is _-_, the receptions are disallowed.
If it is _?_, the state is unknown.
+
[-w?]:::
If the second character is _w_, the transmissions are allowed.
If it is _-_, the transmissions are disallowed.
If it is _?_, the state is unknown.

SOCK.STATE <``string``>::
State of socket.

Expand Down
15 changes: 11 additions & 4 deletions misc-utils/lsfd.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,15 @@ static const struct colinfo infos[] = {
[COL_SOCK_PROTONAME] = { "SOCK.PROTONAME",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("protocol name") },
[COL_SOCK_SHUTDOWN] = { "SOCK.SHUTDOWN",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("shutdown state of socket ([-r?][-w?])") },
[COL_SOCK_STATE] = { "SOCK.STATE",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("State of socket") },
N_("state of socket") },
[COL_SOCK_TYPE] = { "SOCK.TYPE",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("Type of socket") },
N_("type of socket") },
[COL_SOURCE] = { "SOURCE",
0, SCOLS_FL_RIGHT, SCOLS_JSON_STRING,
N_("file system, partition, or device containing file") },
Expand Down Expand Up @@ -2290,11 +2293,15 @@ int main(int argc, char *argv[])
if (scols_table_get_column_by_name(ctl.tb, "XMODE"))
ctl.show_xmode = 1;

/* collect data */
/* collect data
*
* The call initialize_ipc_table() must come before
* initialize_classes.
*/
initialize_nodevs();
initialize_ipc_table();
initialize_classes();
initialize_devdrvs();
initialize_ipc_table();

collect_processes(&ctl, pids, n_pids);
free(pids);
Expand Down
1 change: 1 addition & 0 deletions misc-utils/lsfd.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ enum {
COL_SOCK_LISTENING,
COL_SOCK_NETNS,
COL_SOCK_PROTONAME,
COL_SOCK_SHUTDOWN,
COL_SOCK_STATE,
COL_SOCK_TYPE,
COL_SOURCE,
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions tests/expected/lsfd/mkfds-socketpair-STREAM
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
3 rw- SOCK sockfs UNIX
4 rw- SOCK sockfs UNIX
ASSOC,MODE,STTYPE,SOURCE,SOCK.PROTONAME: 0
Loading

0 comments on commit 36cce3d

Please sign in to comment.