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 struct pid_iter_entry *e; 93 char buf[4096 / sizeof(*e) * sizeof(*e)]; 94 struct pid_iter_bpf *skel; 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 if (errno == EAGAIN) 138 continue; 139 err = -errno; 140 p_err("failed to read PID iterator output: %d", err); 141 goto out; 142 } 143 if (ret == 0) 144 break; 145 if (ret % sizeof(*e)) { 146 err = -EINVAL; 147 p_err("invalid PID iterator output format"); 148 goto out; 149 } 150 ret /= sizeof(*e); 151 152 e = (void *)buf; 153 for (i = 0; i < ret; i++, e++) { 154 add_ref(table, e); 155 } 156 } 157 err = 0; 158 out: 159 if (fd >= 0) 160 close(fd); 161 pid_iter_bpf__destroy(skel); 162 return err; 163 } 164 165 void delete_obj_refs_table(struct obj_refs_table *table) 166 { 167 struct obj_refs *refs; 168 struct hlist_node *tmp; 169 unsigned int bkt; 170 171 hash_for_each_safe(table->table, bkt, tmp, refs, node) { 172 hash_del(&refs->node); 173 free(refs->refs); 174 free(refs); 175 } 176 } 177 178 void emit_obj_refs_json(struct obj_refs_table *table, __u32 id, 179 json_writer_t *json_writer) 180 { 181 struct obj_refs *refs; 182 struct obj_ref *ref; 183 int i; 184 185 if (hash_empty(table->table)) 186 return; 187 188 hash_for_each_possible(table->table, refs, node, id) { 189 if (refs->id != id) 190 continue; 191 if (refs->ref_cnt == 0) 192 break; 193 194 jsonw_name(json_writer, "pids"); 195 jsonw_start_array(json_writer); 196 for (i = 0; i < refs->ref_cnt; i++) { 197 ref = &refs->refs[i]; 198 jsonw_start_object(json_writer); 199 jsonw_int_field(json_writer, "pid", ref->pid); 200 jsonw_string_field(json_writer, "comm", ref->comm); 201 jsonw_end_object(json_writer); 202 } 203 jsonw_end_array(json_writer); 204 break; 205 } 206 } 207 208 void emit_obj_refs_plain(struct obj_refs_table *table, __u32 id, const char *prefix) 209 { 210 struct obj_refs *refs; 211 struct obj_ref *ref; 212 int i; 213 214 if (hash_empty(table->table)) 215 return; 216 217 hash_for_each_possible(table->table, refs, node, id) { 218 if (refs->id != id) 219 continue; 220 if (refs->ref_cnt == 0) 221 break; 222 223 printf("%s", prefix); 224 for (i = 0; i < refs->ref_cnt; i++) { 225 ref = &refs->refs[i]; 226 printf("%s%s(%d)", i == 0 ? "" : ", ", ref->comm, ref->pid); 227 } 228 break; 229 } 230 } 231 232 233 #endif 234