1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 /* Copyright (C) 2020 Facebook */ 3 #include <errno.h> 4 #include <stdbool.h> 5 #include <stdio.h> 6 #include <stdlib.h> 7 #include <string.h> 8 #include <unistd.h> 9 #include <bpf/bpf.h> 10 11 #include "main.h" 12 #include "skeleton/pid_iter.h" 13 14 #ifdef BPFTOOL_WITHOUT_SKELETONS 15 16 int build_obj_refs_table(struct obj_refs_table *table, enum bpf_obj_type type) 17 { 18 return -ENOTSUP; 19 } 20 void delete_obj_refs_table(struct obj_refs_table *table) {} 21 void emit_obj_refs_plain(struct obj_refs_table *table, __u32 id, const char *prefix) {} 22 void emit_obj_refs_json(struct obj_refs_table *table, __u32 id, json_writer_t *json_writer) {} 23 24 #else /* BPFTOOL_WITHOUT_SKELETONS */ 25 26 #include "pid_iter.skel.h" 27 28 static void add_ref(struct obj_refs_table *table, struct pid_iter_entry *e) 29 { 30 struct obj_refs *refs; 31 struct obj_ref *ref; 32 void *tmp; 33 int i; 34 35 hash_for_each_possible(table->table, refs, node, e->id) { 36 if (refs->id != e->id) 37 continue; 38 39 for (i = 0; i < refs->ref_cnt; i++) { 40 if (refs->refs[i].pid == e->pid) 41 return; 42 } 43 44 tmp = realloc(refs->refs, (refs->ref_cnt + 1) * sizeof(*ref)); 45 if (!tmp) { 46 p_err("failed to re-alloc memory for ID %u, PID %d, COMM %s...", 47 e->id, e->pid, e->comm); 48 return; 49 } 50 refs->refs = tmp; 51 ref = &refs->refs[refs->ref_cnt]; 52 ref->pid = e->pid; 53 memcpy(ref->comm, e->comm, sizeof(ref->comm)); 54 refs->ref_cnt++; 55 56 return; 57 } 58 59 /* new ref */ 60 refs = calloc(1, sizeof(*refs)); 61 if (!refs) { 62 p_err("failed to alloc memory for ID %u, PID %d, COMM %s...", 63 e->id, e->pid, e->comm); 64 return; 65 } 66 67 refs->id = e->id; 68 refs->refs = malloc(sizeof(*refs->refs)); 69 if (!refs->refs) { 70 free(refs); 71 p_err("failed to alloc memory for ID %u, PID %d, COMM %s...", 72 e->id, e->pid, e->comm); 73 return; 74 } 75 ref = &refs->refs[0]; 76 ref->pid = e->pid; 77 memcpy(ref->comm, e->comm, sizeof(ref->comm)); 78 refs->ref_cnt = 1; 79 hash_add(table->table, &refs->node, e->id); 80 } 81 82 static int __printf(2, 0) 83 libbpf_print_none(__maybe_unused enum libbpf_print_level level, 84 __maybe_unused const char *format, 85 __maybe_unused va_list args) 86 { 87 return 0; 88 } 89 90 int build_obj_refs_table(struct obj_refs_table *table, enum bpf_obj_type type) 91 { 92 char buf[4096]; 93 struct pid_iter_bpf *skel; 94 struct pid_iter_entry *e; 95 int err, ret, fd = -1, i; 96 libbpf_print_fn_t default_print; 97 98 hash_init(table->table); 99 set_max_rlimit(); 100 101 skel = pid_iter_bpf__open(); 102 if (!skel) { 103 p_err("failed to open PID iterator skeleton"); 104 return -1; 105 } 106 107 skel->rodata->obj_type = type; 108 109 /* we don't want output polluted with libbpf errors if bpf_iter is not 110 * supported 111 */ 112 default_print = libbpf_set_print(libbpf_print_none); 113 err = pid_iter_bpf__load(skel); 114 libbpf_set_print(default_print); 115 if (err) { 116 /* too bad, kernel doesn't support BPF iterators yet */ 117 err = 0; 118 goto out; 119 } 120 err = pid_iter_bpf__attach(skel); 121 if (err) { 122 /* if we loaded above successfully, attach has to succeed */ 123 p_err("failed to attach PID iterator: %d", err); 124 goto out; 125 } 126 127 fd = bpf_iter_create(bpf_link__fd(skel->links.iter)); 128 if (fd < 0) { 129 err = -errno; 130 p_err("failed to create PID iterator session: %d", err); 131 goto out; 132 } 133 134 while (true) { 135 ret = read(fd, buf, sizeof(buf)); 136 if (ret < 0) { 137 err = -errno; 138 p_err("failed to read PID iterator output: %d", err); 139 goto out; 140 } 141 if (ret == 0) 142 break; 143 if (ret % sizeof(*e)) { 144 err = -EINVAL; 145 p_err("invalid PID iterator output format"); 146 goto out; 147 } 148 ret /= sizeof(*e); 149 150 e = (void *)buf; 151 for (i = 0; i < ret; i++, e++) { 152 add_ref(table, e); 153 } 154 } 155 err = 0; 156 out: 157 if (fd >= 0) 158 close(fd); 159 pid_iter_bpf__destroy(skel); 160 return err; 161 } 162 163 void delete_obj_refs_table(struct obj_refs_table *table) 164 { 165 struct obj_refs *refs; 166 struct hlist_node *tmp; 167 unsigned int bkt; 168 169 hash_for_each_safe(table->table, bkt, tmp, refs, node) { 170 hash_del(&refs->node); 171 free(refs->refs); 172 free(refs); 173 } 174 } 175 176 void emit_obj_refs_json(struct obj_refs_table *table, __u32 id, 177 json_writer_t *json_writer) 178 { 179 struct obj_refs *refs; 180 struct obj_ref *ref; 181 int i; 182 183 if (hash_empty(table->table)) 184 return; 185 186 hash_for_each_possible(table->table, refs, node, id) { 187 if (refs->id != id) 188 continue; 189 if (refs->ref_cnt == 0) 190 break; 191 192 jsonw_name(json_writer, "pids"); 193 jsonw_start_array(json_writer); 194 for (i = 0; i < refs->ref_cnt; i++) { 195 ref = &refs->refs[i]; 196 jsonw_start_object(json_writer); 197 jsonw_int_field(json_writer, "pid", ref->pid); 198 jsonw_string_field(json_writer, "comm", ref->comm); 199 jsonw_end_object(json_writer); 200 } 201 jsonw_end_array(json_writer); 202 break; 203 } 204 } 205 206 void emit_obj_refs_plain(struct obj_refs_table *table, __u32 id, const char *prefix) 207 { 208 struct obj_refs *refs; 209 struct obj_ref *ref; 210 int i; 211 212 if (hash_empty(table->table)) 213 return; 214 215 hash_for_each_possible(table->table, refs, node, id) { 216 if (refs->id != id) 217 continue; 218 if (refs->ref_cnt == 0) 219 break; 220 221 printf("%s", prefix); 222 for (i = 0; i < refs->ref_cnt; i++) { 223 ref = &refs->refs[i]; 224 printf("%s%s(%d)", i == 0 ? "" : ", ", ref->comm, ref->pid); 225 } 226 break; 227 } 228 } 229 230 231 #endif 232