text: #include <time.h>
  #include <stdio.h>
  #include <errno.h>
  #include <string.h>
  #include <stdlib.h>
  #include <stdio.h>
  #include <stdint.h>
  #include <unistd.h>
  #include <ctype.h>
  #include <sysexits.h>
  
  #include "libbpf.h"
  
  // TODO: Remove this when CentOS 6 support is not needed anymore
  #ifndef CLOCK_BOOTTIME
  #define CLOCK_BOOTTIME 7
  #endif
  
  static const char * const prog_type_strings[] = {
    [BPF_PROG_TYPE_UNSPEC] = "unspec",
    [BPF_PROG_TYPE_SOCKET_FILTER] = "socket filter",
    [BPF_PROG_TYPE_KPROBE] = "kprobe",
    [BPF_PROG_TYPE_SCHED_CLS] = "sched cls",
    [BPF_PROG_TYPE_SCHED_ACT] = "sched act",
    [BPF_PROG_TYPE_TRACEPOINT] = "tracepoint",
    [BPF_PROG_TYPE_XDP] = "xdp",
    [BPF_PROG_TYPE_PERF_EVENT] = "perf event",
    [BPF_PROG_TYPE_CGROUP_SKB] = "cgroup skb",
    [BPF_PROG_TYPE_CGROUP_SOCK] = "cgroup sock",
    [BPF_PROG_TYPE_LWT_IN] = "lwt in",
    [BPF_PROG_TYPE_LWT_OUT] = "lwt out",
    [BPF_PROG_TYPE_LWT_XMIT] = "lwt xmit",
    [BPF_PROG_TYPE_SOCK_OPS] = "sock ops",
    [BPF_PROG_TYPE_SK_SKB] = "sk skb",
    [BPF_PROG_TYPE_CGROUP_DEVICE] = "cgroup_device",
    [BPF_PROG_TYPE_SK_MSG] = "sk_msg",
    [BPF_PROG_TYPE_RAW_TRACEPOINT] = "raw_tracepoint",
    [BPF_PROG_TYPE_CGROUP_SOCK_ADDR] = "cgroup_sock_addr",
    [BPF_PROG_TYPE_LIRC_MODE2] = "lirc_mode2",
    [BPF_PROG_TYPE_SK_REUSEPORT] = "sk_reuseport",
    [BPF_PROG_TYPE_FLOW_DISSECTOR] = "flow_dissector",
  };
  
  static const char * const map_type_strings[] = {
    [BPF_MAP_TYPE_UNSPEC] = "unspec",
    [BPF_MAP_TYPE_HASH] = "hash",
    [BPF_MAP_TYPE_ARRAY] = "array",
    [BPF_MAP_TYPE_PROG_ARRAY] = "prog array",
    [BPF_MAP_TYPE_PERF_EVENT_ARRAY] = "perf-ev array",
    [BPF_MAP_TYPE_PERCPU_HASH] = "percpu hash",
    [BPF_MAP_TYPE_PERCPU_ARRAY] = "percpu array",
    [BPF_MAP_TYPE_STACK_TRACE] = "stack trace",
    [BPF_MAP_TYPE_CGROUP_ARRAY] = "cgroup array",
    [BPF_MAP_TYPE_LRU_HASH] = "lru hash",
    [BPF_MAP_TYPE_LRU_PERCPU_HASH] = "lru percpu hash",
    [BPF_MAP_TYPE_LPM_TRIE] = "lpm trie",
    [BPF_MAP_TYPE_ARRAY_OF_MAPS] = "array of maps",
    [BPF_MAP_TYPE_HASH_OF_MAPS] = "hash of maps",
    [BPF_MAP_TYPE_DEVMAP] = "devmap",
    [BPF_MAP_TYPE_SOCKMAP] = "sockmap",
    [BPF_MAP_TYPE_CPUMAP] = "cpumap",
    [BPF_MAP_TYPE_SOCKHASH] = "sockhash",
    [BPF_MAP_TYPE_CGROUP_STORAGE] = "cgroup_storage",
    [BPF_MAP_TYPE_REUSEPORT_SOCKARRAY] = "reuseport_sockarray",
  };
  
  #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
  #define LAST_KNOWN_PROG_TYPE (ARRAY_SIZE(prog_type_strings) - 1)
  #define LAST_KNOWN_MAP_TYPE (ARRAY_SIZE(map_type_strings) - 1)
  #define min(x, y) ((x) < (y) ? (x) : (y))
  
  static inline uint64_t ptr_to_u64(const void *ptr)
  {
    return (uint64_t) (unsigned long) ptr;
  }
  
  static inline void * u64_to_ptr(uint64_t ptr)
  {
    return (void *) (unsigned long ) ptr;
  }
  
  static int handle_get_next_errno(int eno)
  {
    switch (eno) {
      case ENOENT:
        return 0;
      case EINVAL:
        fprintf(stderr, "Kernel does not support BPF introspection\n");
        return EX_UNAVAILABLE;
      case EPERM:
        fprintf(stderr,
                "Require CAP_SYS_ADMIN capability.  Please retry as root\n");
        return EX_NOPERM;
      default:
        fprintf(stderr, "%s\n", strerror(errno));
        return 1;
    }
  }
  
  static void print_prog_hdr(void)
  {
    printf("%9s %-15s %8s %6s %-12s %-15s\n",
           "BID", "TYPE", "UID", "#MAPS", "LoadTime", "NAME");
  }
  
  static void print_prog_info(const struct bpf_prog_info *prog_info)
  {
    struct timespec real_time_ts, boot_time_ts;
    time_t wallclock_load_time = 0;
    char unknown_prog_type[16];
    const char *prog_type;
    char load_time[16];
    struct tm load_tm;
  
    if (prog_info->type > LAST_KNOWN_PROG_TYPE) {
      snprintf(unknown_prog_type, sizeof(unknown_prog_type), "<%u>",
               prog_info->type);
      unknown_prog_type[sizeof(unknown_prog_type) - 1] = '\0';
      prog_type = unknown_prog_type;
    } else {
      prog_type = prog_type_strings[prog_info->type];
    }
  
    if (!clock_gettime(CLOCK_REALTIME, &real_time_ts) &&
        !clock_gettime(CLOCK_BOOTTIME, &boot_time_ts) &&
        real_time_ts.tv_sec >= boot_time_ts.tv_sec)
      wallclock_load_time =
        (real_time_ts.tv_sec - boot_time_ts.tv_sec) +
        prog_info->load_time / 1000000000;
  
    if (wallclock_load_time && localtime_r(&wallclock_load_time, &load_tm))
      strftime(load_time, sizeof(load_time), "%b%d/%H:%M", &load_tm);
    else
      snprintf(load_time, sizeof(load_time), "<%llu>",
               prog_info->load_time / 1000000000);
    load_time[sizeof(load_time) - 1] = '\0';
  
    if (prog_info->jited_prog_len)
      printf("%9u %-15s %8u %6u %-12s %-15s\n",
             prog_info->id, prog_type, prog_info->created_by_uid,
             prog_info->nr_map_ids, load_time, prog_info->name);
    else
      printf("%8u- %-15s %8u %6u %-12s %-15s\n",
             prog_info->id, prog_type, prog_info->created_by_uid,
             prog_info->nr_map_ids, load_time, prog_info->name);
  }
  
  static void print_map_hdr(void)
  {
    printf("%8s %-15s %-10s %8s %8s %8s %-15s\n",
           "MID", "TYPE", "FLAGS", "KeySz", "ValueSz", "MaxEnts",
           "NAME");
  }
  
  static void print_map_info(const struct bpf_map_info *map_info)
  {
    char unknown_map_type[16];
    const char *map_type;
  
    if (map_info->type > LAST_KNOWN_MAP_TYPE) {
      snprintf(unknown_map_type, sizeof(unknown_map_type),
               "<%u>", map_info->type);
      unknown_map_type[sizeof(unknown_map_type) - 1] = '\0';
      map_type = unknown_map_type;
    } else {
      map_type = map_type_strings[map_info->type];
    }
  
    printf("%8u %-15s 0x%-8x %8u %8u %8u %-15s\n",
           map_info->id, map_type, map_info->map_flags, map_info->key_size,
           map_info->value_size, map_info->max_entries,
           map_info->name);
  }
  
  static int print_one_prog(uint32_t prog_id)
  {
    const uint32_t usual_nr_map_ids = 64;
    uint32_t nr_map_ids = usual_nr_map_ids;
    struct bpf_prog_info prog_info;
    uint32_t *map_ids =  NULL;
    uint32_t info_len;
    int ret = 0;
    int prog_fd;
    uint32_t i;
  
    prog_fd = bpf_prog_get_fd_by_id(prog_id);
    if (prog_fd == -1) {
      if (errno == ENOENT) {
        fprintf(stderr, "BID:%u not found\n", prog_id);
        return EX_DATAERR;
      } else {
        return handle_get_next_errno(errno);
      }
    }
  
    /* Retry at most one time for larger map_ids array */
    for (i = 0; i < 2; i++) {
      bzero(&prog_info, sizeof(prog_info));
      prog_info.map_ids = ptr_to_u64(realloc(map_ids,
                                             nr_map_ids * sizeof(*map_ids)));
      if (!prog_info.map_ids) {
        fprintf(stderr,
                "Cannot allocate memory for %u map_ids for BID:%u\n",
                nr_map_ids, prog_id);
        close(prog_fd);
        free(map_ids);
        return 1;
      }
  
      map_ids = u64_to_ptr(prog_info.map_ids);
      prog_info.nr_map_ids = nr_map_ids;
      info_len = sizeof(prog_info);
      ret = bpf_obj_get_info(prog_fd, &prog_info, &info_len);
      if (ret) {
        fprintf(stderr, "Cannot get info for BID:%u. %s(%d)\n",
                prog_id, strerror(errno), errno);
        close(prog_fd);
        free(map_ids);
        return ret;
      }
  
      if (prog_info.nr_map_ids <= nr_map_ids)
        break;
  
      nr_map_ids = prog_info.nr_map_ids;
    }
    close(prog_fd);
  
    print_prog_hdr();
    print_prog_info(&prog_info);
    printf("\n");
  
    /* Print all map_info used by the prog */
    print_map_hdr();
    nr_map_ids = min(prog_info.nr_map_ids, nr_map_ids);
    for (i = 0; i < nr_map_ids; i++) {
      struct bpf_map_info map_info = {};
      info_len = sizeof(map_info);
      int map_fd;
  
      map_fd = bpf_map_get_fd_by_id(map_ids[i]);
      if (map_fd == -1) {
        if (errno == -ENOENT)
          continue;
  
        fprintf(stderr,
                "Cannot get fd for map:%u. %s(%d)\n",
                map_ids[i], strerror(errno), errno);
        ret = map_fd;
        break;
      }
  
      ret = bpf_obj_get_info(map_fd, &map_info, &info_len);
      close(map_fd);
      if (ret) {
        fprintf(stderr, "Cannot get info for map:%u. %s(%d)\n",
                map_ids[i], strerror(errno), errno);
        break;
      }
  
      print_map_info(&map_info);
    }
  
    free(map_ids);
    return ret;
  }
  
  int print_all_progs(void)
  {
    uint32_t next_id = 0;
  
    print_prog_hdr();
  
    while (!bpf_prog_get_next_id(next_id, &next_id)) {
      struct bpf_prog_info prog_info = {};
      uint32_t prog_info_len = sizeof(prog_info);
      int prog_fd;
      int ret;
  
      prog_fd = bpf_prog_get_fd_by_id(next_id);
      if (prog_fd < 0) {
        if (errno == ENOENT)
          continue;
        fprintf(stderr,
                "Cannot get fd for BID:%u. %s(%d)\n",
                next_id, strerror(errno), errno);
        return 1;
      }
  
      ret = bpf_obj_get_info(prog_fd, &prog_info, &prog_info_len);
      close(prog_fd);
      if (ret) {
        fprintf(stderr,
                "Cannot get bpf_prog_info for BID:%u. %s(%d)\n",
                next_id, strerror(errno), errno);
        return ret;
      }
  
      print_prog_info(&prog_info);
    }
  
    return handle_get_next_errno(errno);
  }
  
  void usage(void)
  {
    printf("BPF Program Snapshot (bps):\n"
           "List of all BPF programs loaded into the system.\n\n");
    printf("Usage: bps [bpf-prog-id]\n");
    printf("    [bpf-prog-id] If specified, it shows the details info of the bpf-prog\n");
    printf("\n");
  }
  
  int main(int argc, char **argv)
  {
    if (argc > 1) {
      if (!isdigit(*argv[1])) {
        usage();
        return EX_USAGE;
      }
      return print_one_prog((uint32_t)atoi(argv[1]));
    }
  
    return print_all_progs();
  }
  