]> git.ipfire.org Git - thirdparty/util-linux.git/blame - misc-utils/lsfd-sock-xinfo.c
lsfd: cosmetic change, delete whitespaces
[thirdparty/util-linux.git] / misc-utils / lsfd-sock-xinfo.c
CommitLineData
0ee16e43
MY
1/*
2 * lsfd-sock-xinfo.c - read various information from files under /proc/net/
3 *
4 * Copyright (C) 2022 Red Hat, Inc. All rights reserved.
5 * Written by Masatake YAMATO <yamato@redhat.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it would be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
2e38e61c
MY
21#include <fcntl.h> /* open(2) */
22#include <linux/net.h> /* SS_* */
23#include <linux/un.h> /* UNIX_PATH_MAX */
0ee16e43
MY
24#include <sched.h> /* for setns(2) */
25#include <search.h>
2e38e61c
MY
26#include <stdint.h>
27#include <string.h>
28#include <sys/socket.h> /* SOCK_* */
0ee16e43
MY
29
30#include "xalloc.h"
31#include "nls.h"
32#include "libsmartcols.h"
33
34#include "lsfd.h"
35#include "lsfd-sock.h"
36
2e38e61c
MY
37static void load_xinfo_from_proc_unix(ino_t netns_inode);
38
0ee16e43
MY
39static int self_netns_fd = -1;
40struct stat self_netns_sb;
41
42static void *xinfo_tree; /* for tsearch/tfind */
43static void *netns_tree;
44
45static int netns_compare(const void *a, const void *b)
46{
47 if (*(ino_t *)a < *(ino_t *)b)
48 return -1;
49 else if (*(ino_t *)a > *(ino_t *)b)
50 return 1;
51 else
52 return 0;
53}
54
55static bool is_sock_xinfo_loaded(ino_t netns)
56{
57 return tfind(&netns, &netns_tree, netns_compare)? true: false;
58}
59
60static void mark_sock_xinfo_loaded(ino_t ino)
61{
62 ino_t *netns = xmalloc(sizeof(ino));
63 ino_t **tmp;
64
65 *netns = ino;
66 tmp = tsearch(netns, &netns_tree, netns_compare);
67 if (tmp == NULL)
68 errx(EXIT_FAILURE, _("failed to allocate memory"));
69}
70
2e38e61c 71static void load_sock_xinfo_no_nsswitch(ino_t netns)
0ee16e43 72{
2e38e61c 73 load_xinfo_from_proc_unix(netns);
0ee16e43
MY
74}
75
76static void load_sock_xinfo_with_fd(int fd, ino_t netns)
77{
b3649945 78 if (setns(fd, CLONE_NEWNET) == 0) {
0ee16e43 79 load_sock_xinfo_no_nsswitch(netns);
b3649945 80 setns(self_netns_fd, CLONE_NEWNET);
0ee16e43
MY
81 }
82}
83
84void load_sock_xinfo(struct path_cxt *pc, const char *name, ino_t netns)
85{
86 if (self_netns_fd == -1)
87 return;
88
89 if (!is_sock_xinfo_loaded(netns)) {
90 int fd;
91
92 mark_sock_xinfo_loaded(netns);
93 fd = ul_path_open(pc, O_RDONLY, name);
94 if (fd < 0)
95 return;
96
97 load_sock_xinfo_with_fd(fd, netns);
98 close(fd);
99 }
100}
101
102void initialize_sock_xinfos(void)
103{
104 struct path_cxt *pc;
105 DIR *dir;
106 struct dirent *d;
107
108 self_netns_fd = open("/proc/self/ns/net", O_RDONLY);
109
110 if (self_netns_fd < 0)
111 load_sock_xinfo_no_nsswitch(0);
112 else {
113 if (fstat(self_netns_fd, &self_netns_sb) == 0) {
114 mark_sock_xinfo_loaded(self_netns_sb.st_ino);
115 load_sock_xinfo_no_nsswitch(self_netns_sb.st_ino);
116 }
117 }
118
119 /* Load /proc/net/{unix,...} of the network namespace
120 * specified with netns files under /var/run/netns/.
121 *
122 * `ip netns' command pins a network namespace on
123 * /var/run/netns.
124 */
125 pc = ul_new_path("/var/run/netns");
126 if (!pc)
127 err(EXIT_FAILURE, _("failed to alloc path context for /var/run/netns"));
128 dir = ul_path_opendir(pc, NULL);
129 if (dir == NULL) {
130 ul_unref_path(pc);
131 return;
132 }
133 while ((d = readdir(dir))) {
134 struct stat sb;
135 int fd;
136 if (ul_path_stat(pc, &sb, 0, d->d_name) < 0)
137 continue;
138 if (is_sock_xinfo_loaded(sb.st_ino))
139 continue;
140 mark_sock_xinfo_loaded(sb.st_ino);
141 fd = ul_path_open(pc, O_RDONLY, d->d_name);
142 if (fd < 0)
143 continue;
144 load_sock_xinfo_with_fd(fd, sb.st_ino);
145 close(fd);
146 }
147 closedir(dir);
148 ul_unref_path(pc);
149}
150
b3649945 151static void free_sock_xinfo(void *node)
0ee16e43
MY
152{
153 struct sock_xinfo *xinfo = node;
154 if (xinfo->class->free)
b3649945 155 xinfo->class->free(xinfo);
0ee16e43
MY
156 free(node);
157}
158
159void finalize_sock_xinfos(void)
160{
161 if (self_netns_fd != -1)
162 close(self_netns_fd);
163 tdestroy(netns_tree, free);
164 tdestroy(xinfo_tree, free_sock_xinfo);
165}
166
167static int xinfo_compare(const void *a, const void *b)
168{
169 if (((struct sock_xinfo *)a)->inode < ((struct sock_xinfo *)b)->inode)
170 return -1;
171 if (((struct sock_xinfo *)a)->inode > ((struct sock_xinfo *)b)->inode)
172 return 1;
173 return 0;
174}
175
2e38e61c
MY
176static void add_sock_info(struct sock_xinfo *xinfo)
177{
178 struct sock_xinfo **tmp = tsearch(xinfo, &xinfo_tree, xinfo_compare);
179
180 if (tmp == NULL)
181 errx(EXIT_FAILURE, _("failed to allocate memory"));
182}
183
0ee16e43
MY
184struct sock_xinfo *get_sock_xinfo(ino_t netns_inode)
185{
186 struct sock_xinfo **xinfo = tfind(&netns_inode, &xinfo_tree, xinfo_compare);
187
188 if (xinfo)
189 return *xinfo;
190 return NULL;
191}
192
193bool is_nsfs_dev(dev_t dev)
194{
195 return (dev == self_netns_sb.st_dev);
196}
2e38e61c
MY
197
198static const char *sock_decode_type(uint16_t type)
199{
200 switch (type) {
201 case SOCK_STREAM:
202 return "stream";
203 case SOCK_DGRAM:
204 return "dgram";
205 case SOCK_RAW:
206 return "raw";
207 case SOCK_RDM:
208 return "rdm";
209 case SOCK_SEQPACKET:
210 return "seqpacket";
211 case SOCK_DCCP:
212 return "dccp";
213 case SOCK_PACKET:
214 return "packet";
215 default:
216 return "unknown";
217 }
218}
219
220/*
221 * Protocol specific code
222 */
223
224/*
225 * UNIX
226 */
227struct unix_xinfo {
228 struct sock_xinfo sock;
229 int acceptcon; /* flags */
230 uint16_t type;
231 uint8_t st;
232 char path[
233 UNIX_PATH_MAX
234 + 1 /* for @ */
235 + 1 /* \0? */
236 ];
237};
238
239static const char *unix_decode_state(uint8_t st)
240{
241 switch (st) {
242 case SS_FREE:
243 return "free";
244 case SS_UNCONNECTED:
245 return "unconnected";
246 case SS_CONNECTING:
247 return "connecting";
248 case SS_CONNECTED:
249 return "connected";
250 case SS_DISCONNECTING:
251 return "disconnecting";
252 default:
253 return "unknown";
254 }
255}
256
257static char *unix_get_name(struct sock_xinfo *sock_xinfo,
258 struct sock *sock)
259{
260 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
261 const char *state = unix_decode_state(ux->st);
262 char *str = NULL;
263
264 if (sock->protoname && (strcmp(sock->protoname, "UNIX-STREAM") == 0))
265 xasprintf(&str, "state=%s%s%s",
266 (ux->acceptcon)? "listen": state,
267 *(ux->path)? " path=": "",
268 *(ux->path)? ux->path: "");
269 else
270 xasprintf(&str, "state=%s%s%s type=%s",
271 (ux->acceptcon)? "listen": state,
272 *(ux->path)? " path=": "",
273 *(ux->path)? ux->path: "",
274 sock_decode_type(ux->type));
275 return str;
276}
277
278static char *unix_get_type(struct sock_xinfo *sock_xinfo,
279 struct sock *sock __attribute__((__unused__)))
280{
281 const char *str;
282 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
283
284 str = sock_decode_type(ux->type);
285 return strdup(str);
286}
287
288static char *unix_get_state(struct sock_xinfo *sock_xinfo,
289 struct sock *sock __attribute__((__unused__)))
290{
291 const char *str;
292 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
293
294 if (ux->acceptcon)
295 return strdup("listen");
296
297 str = unix_decode_state(ux->st);
298 return strdup(str);
299}
300
45d61bff
MY
301static bool unix_get_listening(struct sock_xinfo *sock_xinfo,
302 struct sock *sock __attribute__((__unused__)))
303{
304 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
305
306 return ux->acceptcon;
307}
308
2e38e61c
MY
309static bool unix_fill_column(struct proc *proc __attribute__((__unused__)),
310 struct sock_xinfo *sock_xinfo,
311 struct sock *sock __attribute__((__unused__)),
312 struct libscols_line *ln __attribute__((__unused__)),
313 int column_id,
314 size_t column_index __attribute__((__unused__)),
315 char **str)
316{
317 struct unix_xinfo *ux = (struct unix_xinfo *)sock_xinfo;
318
319 switch(column_id) {
320 case COL_UNIX_PATH:
321 if (*ux->path) {
322 *str = strdup(ux->path);
323 return true;
324 }
325 break;
326 }
327
328 return false;
329}
330
331static struct sock_xinfo_class unix_xinfo_class = {
332 .class = "unix",
333 .get_name = unix_get_name,
334 .get_type = unix_get_type,
335 .get_state = unix_get_state,
45d61bff 336 .get_listening = unix_get_listening,
2e38e61c
MY
337 .fill_column = unix_fill_column,
338 .free = NULL,
339};
340
341/* #define UNIX_LINE_LEN 54 + 21 + UNIX_LINE_LEN + 1 */
342#define UNIX_LINE_LEN 256
343static void load_xinfo_from_proc_unix(ino_t netns_inode)
344{
345 char line[UNIX_LINE_LEN];
346 FILE *unix_fp;
347
348 unix_fp = fopen("/proc/net/unix", "r");
349 if (!unix_fp)
350 return;
351
352 if (fgets(line, sizeof(line), unix_fp) == NULL)
353 goto out;
b3649945 354 if (!(line[0] == 'N' && line[1] == 'u' && line[2] == 'm'))
2e38e61c
MY
355 /* Unexpected line */
356 goto out;
357
358 while (fgets(line, sizeof(line), unix_fp)) {
359 uint64_t flags;
360 uint32_t type;
361 unsigned int st;
362 unsigned long inode;
363 char path[1 + UNIX_PATH_MAX +1];
364 struct unix_xinfo *ux;
365
366 memset(path, 0, sizeof(path));
367 if (sscanf(line, "%*x: %*x %*x %lx %x %x %lu %s",
368 &flags, &type, &st, &inode, path) < 4)
369 continue;
370
371 if (inode == 0)
372 continue;
373
374 ux = xmalloc(sizeof(struct unix_xinfo));
375 ux->sock.class = &unix_xinfo_class;
376 ux->sock.inode = (ino_t)inode;
377 ux->sock.netns_inode = netns_inode;
378
379 ux->acceptcon = !!flags;
380 ux->type = type;
381 ux->st = st;
382 strcpy(ux->path, path);
383
384 add_sock_info((struct sock_xinfo *)ux);
385 }
386
387 out:
388 fclose(unix_fp);
389}