xref: /OK3568_Linux_fs/kernel/tools/bpf/bpftool/link.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2*4882a593Smuzhiyun /* Copyright (C) 2020 Facebook */
3*4882a593Smuzhiyun 
4*4882a593Smuzhiyun #include <errno.h>
5*4882a593Smuzhiyun #include <net/if.h>
6*4882a593Smuzhiyun #include <stdio.h>
7*4882a593Smuzhiyun #include <unistd.h>
8*4882a593Smuzhiyun 
9*4882a593Smuzhiyun #include <bpf/bpf.h>
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun #include "json_writer.h"
12*4882a593Smuzhiyun #include "main.h"
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun static const char * const link_type_name[] = {
15*4882a593Smuzhiyun 	[BPF_LINK_TYPE_UNSPEC]			= "unspec",
16*4882a593Smuzhiyun 	[BPF_LINK_TYPE_RAW_TRACEPOINT]		= "raw_tracepoint",
17*4882a593Smuzhiyun 	[BPF_LINK_TYPE_TRACING]			= "tracing",
18*4882a593Smuzhiyun 	[BPF_LINK_TYPE_CGROUP]			= "cgroup",
19*4882a593Smuzhiyun 	[BPF_LINK_TYPE_ITER]			= "iter",
20*4882a593Smuzhiyun 	[BPF_LINK_TYPE_NETNS]			= "netns",
21*4882a593Smuzhiyun };
22*4882a593Smuzhiyun 
link_parse_fd(int * argc,char *** argv)23*4882a593Smuzhiyun static int link_parse_fd(int *argc, char ***argv)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun 	int fd;
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun 	if (is_prefix(**argv, "id")) {
28*4882a593Smuzhiyun 		unsigned int id;
29*4882a593Smuzhiyun 		char *endptr;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 		NEXT_ARGP();
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 		id = strtoul(**argv, &endptr, 0);
34*4882a593Smuzhiyun 		if (*endptr) {
35*4882a593Smuzhiyun 			p_err("can't parse %s as ID", **argv);
36*4882a593Smuzhiyun 			return -1;
37*4882a593Smuzhiyun 		}
38*4882a593Smuzhiyun 		NEXT_ARGP();
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 		fd = bpf_link_get_fd_by_id(id);
41*4882a593Smuzhiyun 		if (fd < 0)
42*4882a593Smuzhiyun 			p_err("failed to get link with ID %d: %s", id, strerror(errno));
43*4882a593Smuzhiyun 		return fd;
44*4882a593Smuzhiyun 	} else if (is_prefix(**argv, "pinned")) {
45*4882a593Smuzhiyun 		char *path;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 		NEXT_ARGP();
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 		path = **argv;
50*4882a593Smuzhiyun 		NEXT_ARGP();
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 		return open_obj_pinned_any(path, BPF_OBJ_LINK);
53*4882a593Smuzhiyun 	}
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun 	p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
56*4882a593Smuzhiyun 	return -1;
57*4882a593Smuzhiyun }
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun static void
show_link_header_json(struct bpf_link_info * info,json_writer_t * wtr)60*4882a593Smuzhiyun show_link_header_json(struct bpf_link_info *info, json_writer_t *wtr)
61*4882a593Smuzhiyun {
62*4882a593Smuzhiyun 	jsonw_uint_field(wtr, "id", info->id);
63*4882a593Smuzhiyun 	if (info->type < ARRAY_SIZE(link_type_name))
64*4882a593Smuzhiyun 		jsonw_string_field(wtr, "type", link_type_name[info->type]);
65*4882a593Smuzhiyun 	else
66*4882a593Smuzhiyun 		jsonw_uint_field(wtr, "type", info->type);
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 	jsonw_uint_field(json_wtr, "prog_id", info->prog_id);
69*4882a593Smuzhiyun }
70*4882a593Smuzhiyun 
show_link_attach_type_json(__u32 attach_type,json_writer_t * wtr)71*4882a593Smuzhiyun static void show_link_attach_type_json(__u32 attach_type, json_writer_t *wtr)
72*4882a593Smuzhiyun {
73*4882a593Smuzhiyun 	if (attach_type < ARRAY_SIZE(attach_type_name))
74*4882a593Smuzhiyun 		jsonw_string_field(wtr, "attach_type",
75*4882a593Smuzhiyun 				   attach_type_name[attach_type]);
76*4882a593Smuzhiyun 	else
77*4882a593Smuzhiyun 		jsonw_uint_field(wtr, "attach_type", attach_type);
78*4882a593Smuzhiyun }
79*4882a593Smuzhiyun 
is_iter_map_target(const char * target_name)80*4882a593Smuzhiyun static bool is_iter_map_target(const char *target_name)
81*4882a593Smuzhiyun {
82*4882a593Smuzhiyun 	return strcmp(target_name, "bpf_map_elem") == 0 ||
83*4882a593Smuzhiyun 	       strcmp(target_name, "bpf_sk_storage_map") == 0;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun 
show_iter_json(struct bpf_link_info * info,json_writer_t * wtr)86*4882a593Smuzhiyun static void show_iter_json(struct bpf_link_info *info, json_writer_t *wtr)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun 	const char *target_name = u64_to_ptr(info->iter.target_name);
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	jsonw_string_field(wtr, "target_name", target_name);
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun 	if (is_iter_map_target(target_name))
93*4882a593Smuzhiyun 		jsonw_uint_field(wtr, "map_id", info->iter.map.map_id);
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun 
get_prog_info(int prog_id,struct bpf_prog_info * info)96*4882a593Smuzhiyun static int get_prog_info(int prog_id, struct bpf_prog_info *info)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun 	__u32 len = sizeof(*info);
99*4882a593Smuzhiyun 	int err, prog_fd;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	prog_fd = bpf_prog_get_fd_by_id(prog_id);
102*4882a593Smuzhiyun 	if (prog_fd < 0)
103*4882a593Smuzhiyun 		return prog_fd;
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	memset(info, 0, sizeof(*info));
106*4882a593Smuzhiyun 	err = bpf_obj_get_info_by_fd(prog_fd, info, &len);
107*4882a593Smuzhiyun 	if (err)
108*4882a593Smuzhiyun 		p_err("can't get prog info: %s", strerror(errno));
109*4882a593Smuzhiyun 	close(prog_fd);
110*4882a593Smuzhiyun 	return err;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
show_link_close_json(int fd,struct bpf_link_info * info)113*4882a593Smuzhiyun static int show_link_close_json(int fd, struct bpf_link_info *info)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun 	struct bpf_prog_info prog_info;
116*4882a593Smuzhiyun 	int err;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	jsonw_start_object(json_wtr);
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	show_link_header_json(info, json_wtr);
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun 	switch (info->type) {
123*4882a593Smuzhiyun 	case BPF_LINK_TYPE_RAW_TRACEPOINT:
124*4882a593Smuzhiyun 		jsonw_string_field(json_wtr, "tp_name",
125*4882a593Smuzhiyun 				   u64_to_ptr(info->raw_tracepoint.tp_name));
126*4882a593Smuzhiyun 		break;
127*4882a593Smuzhiyun 	case BPF_LINK_TYPE_TRACING:
128*4882a593Smuzhiyun 		err = get_prog_info(info->prog_id, &prog_info);
129*4882a593Smuzhiyun 		if (err)
130*4882a593Smuzhiyun 			return err;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun 		if (prog_info.type < prog_type_name_size)
133*4882a593Smuzhiyun 			jsonw_string_field(json_wtr, "prog_type",
134*4882a593Smuzhiyun 					   prog_type_name[prog_info.type]);
135*4882a593Smuzhiyun 		else
136*4882a593Smuzhiyun 			jsonw_uint_field(json_wtr, "prog_type",
137*4882a593Smuzhiyun 					 prog_info.type);
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 		show_link_attach_type_json(info->tracing.attach_type,
140*4882a593Smuzhiyun 					   json_wtr);
141*4882a593Smuzhiyun 		break;
142*4882a593Smuzhiyun 	case BPF_LINK_TYPE_CGROUP:
143*4882a593Smuzhiyun 		jsonw_lluint_field(json_wtr, "cgroup_id",
144*4882a593Smuzhiyun 				   info->cgroup.cgroup_id);
145*4882a593Smuzhiyun 		show_link_attach_type_json(info->cgroup.attach_type, json_wtr);
146*4882a593Smuzhiyun 		break;
147*4882a593Smuzhiyun 	case BPF_LINK_TYPE_ITER:
148*4882a593Smuzhiyun 		show_iter_json(info, json_wtr);
149*4882a593Smuzhiyun 		break;
150*4882a593Smuzhiyun 	case BPF_LINK_TYPE_NETNS:
151*4882a593Smuzhiyun 		jsonw_uint_field(json_wtr, "netns_ino",
152*4882a593Smuzhiyun 				 info->netns.netns_ino);
153*4882a593Smuzhiyun 		show_link_attach_type_json(info->netns.attach_type, json_wtr);
154*4882a593Smuzhiyun 		break;
155*4882a593Smuzhiyun 	default:
156*4882a593Smuzhiyun 		break;
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	if (!hash_empty(link_table.table)) {
160*4882a593Smuzhiyun 		struct pinned_obj *obj;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 		jsonw_name(json_wtr, "pinned");
163*4882a593Smuzhiyun 		jsonw_start_array(json_wtr);
164*4882a593Smuzhiyun 		hash_for_each_possible(link_table.table, obj, hash, info->id) {
165*4882a593Smuzhiyun 			if (obj->id == info->id)
166*4882a593Smuzhiyun 				jsonw_string(json_wtr, obj->path);
167*4882a593Smuzhiyun 		}
168*4882a593Smuzhiyun 		jsonw_end_array(json_wtr);
169*4882a593Smuzhiyun 	}
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	emit_obj_refs_json(&refs_table, info->id, json_wtr);
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	jsonw_end_object(json_wtr);
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 	return 0;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
show_link_header_plain(struct bpf_link_info * info)178*4882a593Smuzhiyun static void show_link_header_plain(struct bpf_link_info *info)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	printf("%u: ", info->id);
181*4882a593Smuzhiyun 	if (info->type < ARRAY_SIZE(link_type_name))
182*4882a593Smuzhiyun 		printf("%s  ", link_type_name[info->type]);
183*4882a593Smuzhiyun 	else
184*4882a593Smuzhiyun 		printf("type %u  ", info->type);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	printf("prog %u  ", info->prog_id);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun 
show_link_attach_type_plain(__u32 attach_type)189*4882a593Smuzhiyun static void show_link_attach_type_plain(__u32 attach_type)
190*4882a593Smuzhiyun {
191*4882a593Smuzhiyun 	if (attach_type < ARRAY_SIZE(attach_type_name))
192*4882a593Smuzhiyun 		printf("attach_type %s  ", attach_type_name[attach_type]);
193*4882a593Smuzhiyun 	else
194*4882a593Smuzhiyun 		printf("attach_type %u  ", attach_type);
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
show_iter_plain(struct bpf_link_info * info)197*4882a593Smuzhiyun static void show_iter_plain(struct bpf_link_info *info)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	const char *target_name = u64_to_ptr(info->iter.target_name);
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun 	printf("target_name %s  ", target_name);
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	if (is_iter_map_target(target_name))
204*4882a593Smuzhiyun 		printf("map_id %u  ", info->iter.map.map_id);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun 
show_link_close_plain(int fd,struct bpf_link_info * info)207*4882a593Smuzhiyun static int show_link_close_plain(int fd, struct bpf_link_info *info)
208*4882a593Smuzhiyun {
209*4882a593Smuzhiyun 	struct bpf_prog_info prog_info;
210*4882a593Smuzhiyun 	int err;
211*4882a593Smuzhiyun 
212*4882a593Smuzhiyun 	show_link_header_plain(info);
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 	switch (info->type) {
215*4882a593Smuzhiyun 	case BPF_LINK_TYPE_RAW_TRACEPOINT:
216*4882a593Smuzhiyun 		printf("\n\ttp '%s'  ",
217*4882a593Smuzhiyun 		       (const char *)u64_to_ptr(info->raw_tracepoint.tp_name));
218*4882a593Smuzhiyun 		break;
219*4882a593Smuzhiyun 	case BPF_LINK_TYPE_TRACING:
220*4882a593Smuzhiyun 		err = get_prog_info(info->prog_id, &prog_info);
221*4882a593Smuzhiyun 		if (err)
222*4882a593Smuzhiyun 			return err;
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 		if (prog_info.type < prog_type_name_size)
225*4882a593Smuzhiyun 			printf("\n\tprog_type %s  ",
226*4882a593Smuzhiyun 			       prog_type_name[prog_info.type]);
227*4882a593Smuzhiyun 		else
228*4882a593Smuzhiyun 			printf("\n\tprog_type %u  ", prog_info.type);
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 		show_link_attach_type_plain(info->tracing.attach_type);
231*4882a593Smuzhiyun 		break;
232*4882a593Smuzhiyun 	case BPF_LINK_TYPE_CGROUP:
233*4882a593Smuzhiyun 		printf("\n\tcgroup_id %zu  ", (size_t)info->cgroup.cgroup_id);
234*4882a593Smuzhiyun 		show_link_attach_type_plain(info->cgroup.attach_type);
235*4882a593Smuzhiyun 		break;
236*4882a593Smuzhiyun 	case BPF_LINK_TYPE_ITER:
237*4882a593Smuzhiyun 		show_iter_plain(info);
238*4882a593Smuzhiyun 		break;
239*4882a593Smuzhiyun 	case BPF_LINK_TYPE_NETNS:
240*4882a593Smuzhiyun 		printf("\n\tnetns_ino %u  ", info->netns.netns_ino);
241*4882a593Smuzhiyun 		show_link_attach_type_plain(info->netns.attach_type);
242*4882a593Smuzhiyun 		break;
243*4882a593Smuzhiyun 	default:
244*4882a593Smuzhiyun 		break;
245*4882a593Smuzhiyun 	}
246*4882a593Smuzhiyun 
247*4882a593Smuzhiyun 	if (!hash_empty(link_table.table)) {
248*4882a593Smuzhiyun 		struct pinned_obj *obj;
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 		hash_for_each_possible(link_table.table, obj, hash, info->id) {
251*4882a593Smuzhiyun 			if (obj->id == info->id)
252*4882a593Smuzhiyun 				printf("\n\tpinned %s", obj->path);
253*4882a593Smuzhiyun 		}
254*4882a593Smuzhiyun 	}
255*4882a593Smuzhiyun 	emit_obj_refs_plain(&refs_table, info->id, "\n\tpids ");
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	printf("\n");
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	return 0;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun 
do_show_link(int fd)262*4882a593Smuzhiyun static int do_show_link(int fd)
263*4882a593Smuzhiyun {
264*4882a593Smuzhiyun 	struct bpf_link_info info;
265*4882a593Smuzhiyun 	__u32 len = sizeof(info);
266*4882a593Smuzhiyun 	char buf[256];
267*4882a593Smuzhiyun 	int err;
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	memset(&info, 0, sizeof(info));
270*4882a593Smuzhiyun again:
271*4882a593Smuzhiyun 	err = bpf_obj_get_info_by_fd(fd, &info, &len);
272*4882a593Smuzhiyun 	if (err) {
273*4882a593Smuzhiyun 		p_err("can't get link info: %s",
274*4882a593Smuzhiyun 		      strerror(errno));
275*4882a593Smuzhiyun 		close(fd);
276*4882a593Smuzhiyun 		return err;
277*4882a593Smuzhiyun 	}
278*4882a593Smuzhiyun 	if (info.type == BPF_LINK_TYPE_RAW_TRACEPOINT &&
279*4882a593Smuzhiyun 	    !info.raw_tracepoint.tp_name) {
280*4882a593Smuzhiyun 		info.raw_tracepoint.tp_name = (unsigned long)&buf;
281*4882a593Smuzhiyun 		info.raw_tracepoint.tp_name_len = sizeof(buf);
282*4882a593Smuzhiyun 		goto again;
283*4882a593Smuzhiyun 	}
284*4882a593Smuzhiyun 	if (info.type == BPF_LINK_TYPE_ITER &&
285*4882a593Smuzhiyun 	    !info.iter.target_name) {
286*4882a593Smuzhiyun 		info.iter.target_name = (unsigned long)&buf;
287*4882a593Smuzhiyun 		info.iter.target_name_len = sizeof(buf);
288*4882a593Smuzhiyun 		goto again;
289*4882a593Smuzhiyun 	}
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	if (json_output)
292*4882a593Smuzhiyun 		show_link_close_json(fd, &info);
293*4882a593Smuzhiyun 	else
294*4882a593Smuzhiyun 		show_link_close_plain(fd, &info);
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun 	close(fd);
297*4882a593Smuzhiyun 	return 0;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun 
do_show(int argc,char ** argv)300*4882a593Smuzhiyun static int do_show(int argc, char **argv)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	__u32 id = 0;
303*4882a593Smuzhiyun 	int err, fd;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	if (show_pinned)
306*4882a593Smuzhiyun 		build_pinned_obj_table(&link_table, BPF_OBJ_LINK);
307*4882a593Smuzhiyun 	build_obj_refs_table(&refs_table, BPF_OBJ_LINK);
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 	if (argc == 2) {
310*4882a593Smuzhiyun 		fd = link_parse_fd(&argc, &argv);
311*4882a593Smuzhiyun 		if (fd < 0)
312*4882a593Smuzhiyun 			return fd;
313*4882a593Smuzhiyun 		return do_show_link(fd);
314*4882a593Smuzhiyun 	}
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	if (argc)
317*4882a593Smuzhiyun 		return BAD_ARG();
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	if (json_output)
320*4882a593Smuzhiyun 		jsonw_start_array(json_wtr);
321*4882a593Smuzhiyun 	while (true) {
322*4882a593Smuzhiyun 		err = bpf_link_get_next_id(id, &id);
323*4882a593Smuzhiyun 		if (err) {
324*4882a593Smuzhiyun 			if (errno == ENOENT)
325*4882a593Smuzhiyun 				break;
326*4882a593Smuzhiyun 			p_err("can't get next link: %s%s", strerror(errno),
327*4882a593Smuzhiyun 			      errno == EINVAL ? " -- kernel too old?" : "");
328*4882a593Smuzhiyun 			break;
329*4882a593Smuzhiyun 		}
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 		fd = bpf_link_get_fd_by_id(id);
332*4882a593Smuzhiyun 		if (fd < 0) {
333*4882a593Smuzhiyun 			if (errno == ENOENT)
334*4882a593Smuzhiyun 				continue;
335*4882a593Smuzhiyun 			p_err("can't get link by id (%u): %s",
336*4882a593Smuzhiyun 			      id, strerror(errno));
337*4882a593Smuzhiyun 			break;
338*4882a593Smuzhiyun 		}
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 		err = do_show_link(fd);
341*4882a593Smuzhiyun 		if (err)
342*4882a593Smuzhiyun 			break;
343*4882a593Smuzhiyun 	}
344*4882a593Smuzhiyun 	if (json_output)
345*4882a593Smuzhiyun 		jsonw_end_array(json_wtr);
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	delete_obj_refs_table(&refs_table);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	return errno == ENOENT ? 0 : -1;
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
do_pin(int argc,char ** argv)352*4882a593Smuzhiyun static int do_pin(int argc, char **argv)
353*4882a593Smuzhiyun {
354*4882a593Smuzhiyun 	int err;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	err = do_pin_any(argc, argv, link_parse_fd);
357*4882a593Smuzhiyun 	if (!err && json_output)
358*4882a593Smuzhiyun 		jsonw_null(json_wtr);
359*4882a593Smuzhiyun 	return err;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
do_detach(int argc,char ** argv)362*4882a593Smuzhiyun static int do_detach(int argc, char **argv)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun 	int err, fd;
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	if (argc != 2) {
367*4882a593Smuzhiyun 		p_err("link specifier is invalid or missing\n");
368*4882a593Smuzhiyun 		return 1;
369*4882a593Smuzhiyun 	}
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	fd = link_parse_fd(&argc, &argv);
372*4882a593Smuzhiyun 	if (fd < 0)
373*4882a593Smuzhiyun 		return 1;
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	err = bpf_link_detach(fd);
376*4882a593Smuzhiyun 	if (err)
377*4882a593Smuzhiyun 		err = -errno;
378*4882a593Smuzhiyun 	close(fd);
379*4882a593Smuzhiyun 	if (err) {
380*4882a593Smuzhiyun 		p_err("failed link detach: %s", strerror(-err));
381*4882a593Smuzhiyun 		return 1;
382*4882a593Smuzhiyun 	}
383*4882a593Smuzhiyun 
384*4882a593Smuzhiyun 	if (json_output)
385*4882a593Smuzhiyun 		jsonw_null(json_wtr);
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	return 0;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun 
do_help(int argc,char ** argv)390*4882a593Smuzhiyun static int do_help(int argc, char **argv)
391*4882a593Smuzhiyun {
392*4882a593Smuzhiyun 	if (json_output) {
393*4882a593Smuzhiyun 		jsonw_null(json_wtr);
394*4882a593Smuzhiyun 		return 0;
395*4882a593Smuzhiyun 	}
396*4882a593Smuzhiyun 
397*4882a593Smuzhiyun 	fprintf(stderr,
398*4882a593Smuzhiyun 		"Usage: %1$s %2$s { show | list }   [LINK]\n"
399*4882a593Smuzhiyun 		"       %1$s %2$s pin        LINK  FILE\n"
400*4882a593Smuzhiyun 		"       %1$s %2$s detach     LINK\n"
401*4882a593Smuzhiyun 		"       %1$s %2$s help\n"
402*4882a593Smuzhiyun 		"\n"
403*4882a593Smuzhiyun 		"       " HELP_SPEC_LINK "\n"
404*4882a593Smuzhiyun 		"       " HELP_SPEC_OPTIONS "\n"
405*4882a593Smuzhiyun 		"",
406*4882a593Smuzhiyun 		bin_name, argv[-2]);
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	return 0;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun static const struct cmd cmds[] = {
412*4882a593Smuzhiyun 	{ "show",	do_show },
413*4882a593Smuzhiyun 	{ "list",	do_show },
414*4882a593Smuzhiyun 	{ "help",	do_help },
415*4882a593Smuzhiyun 	{ "pin",	do_pin },
416*4882a593Smuzhiyun 	{ "detach",	do_detach },
417*4882a593Smuzhiyun 	{ 0 }
418*4882a593Smuzhiyun };
419*4882a593Smuzhiyun 
do_link(int argc,char ** argv)420*4882a593Smuzhiyun int do_link(int argc, char **argv)
421*4882a593Smuzhiyun {
422*4882a593Smuzhiyun 	return cmd_select(cmds, argc, argv, do_help);
423*4882a593Smuzhiyun }
424