github.com/containers/podman/v5@v5.1.0-rc1/hack/podmansnoop (about)

     1  #!/usr/bin/env python
     2  from __future__ import print_function
     3  
     4  # Based on the `exitsnoop` script at https://github.com/iovisor/bcc/blob/master/tools/exitsnoop.py
     5  # Based on the `execsnoop` script at https://github.com/iovisor/bcc/blob/master/tools/execsnoop.py
     6  
     7  import argparse
     8  import os
     9  import platform
    10  import re
    11  import signal
    12  import sys
    13  
    14  from bcc import BPF
    15  from collections import defaultdict
    16  from datetime import datetime
    17  from time import strftime
    18  
    19  bpf_src = """
    20  #include <uapi/linux/ptrace.h>
    21  #include <linux/sched.h>
    22  
    23  #define ARGSIZE 128
    24  #define MAXARG  19
    25  
    26  struct data_t {
    27      u8 isArgv;
    28      u64 start_time;
    29      u64 exit_time;
    30      u32 pid;
    31      u32 ppid;
    32      char comm[TASK_COMM_LEN];
    33      char argv[ARGSIZE];
    34  };
    35  
    36  BPF_PERF_OUTPUT(events);
    37  
    38  TRACEPOINT_PROBE(sched, sched_process_exit)
    39  {
    40      struct task_struct *task = (typeof(task))bpf_get_current_task();
    41      if (task->pid != task->tgid) { return 0; }
    42  
    43      struct data_t data = {};
    44  
    45      data.start_time = task->start_time,
    46      data.exit_time = bpf_ktime_get_ns(),
    47      data.pid = task->pid,
    48      data.ppid = task->real_parent->tgid,
    49      bpf_get_current_comm(&data.comm, sizeof(data.comm));
    50  
    51      events.perf_submit(args, &data, sizeof(data));
    52      return 0;
    53  }
    54  
    55  static int __submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data)
    56  {
    57      bpf_probe_read_user(data->argv, sizeof(data->argv), ptr);
    58      events.perf_submit(ctx, data, sizeof(struct data_t));
    59      return 1;
    60  }
    61  
    62  static int submit_arg(struct pt_regs *ctx, void *ptr, struct data_t *data)
    63  {
    64      const char *argp = NULL;
    65      bpf_probe_read_user(&argp, sizeof(argp), ptr);
    66      if (argp) {
    67          return __submit_arg(ctx, (void *)(argp), data);
    68      }
    69      return 0;
    70  }
    71  
    72  int syscall__execve(struct pt_regs *ctx,
    73      const char __user *filename,
    74      const char __user *const __user *__argv,
    75      const char __user *const __user *__envp)
    76  {
    77  
    78      struct task_struct *task = (typeof(task))bpf_get_current_task();
    79  
    80      struct data_t data = {};
    81      data.pid = bpf_get_current_pid_tgid() >> 32;
    82      data.isArgv = 1;
    83  
    84      bpf_get_current_comm(&data.comm, sizeof(data.comm));
    85      __submit_arg(ctx, (void *)filename, &data);
    86  
    87      // skip first arg, as we submitted filename
    88      #pragma unroll
    89      for (int i = 1; i < MAXARG; i++) {
    90          if (submit_arg(ctx, (void *)&__argv[i], &data) == 0)
    91               goto out;
    92      }
    93  
    94      // handle truncated argument list
    95      char ellipsis[] = "...";
    96      __submit_arg(ctx, (void *)ellipsis, &data);
    97  out:
    98  
    99      return 0;
   100  }
   101  
   102  int do_ret_sys_execve(struct pt_regs *ctx)
   103  {
   104      struct task_struct *task = (typeof(task))bpf_get_current_task();
   105  
   106      struct data_t data = {};
   107      data.pid = bpf_get_current_pid_tgid() >> 32;
   108      bpf_get_current_comm(&data.comm, sizeof(data.comm));
   109      events.perf_submit(ctx, &data, sizeof(data));
   110      return 0;
   111  }
   112  """
   113  
   114  def _print_header():
   115      print("%-16s %-7s %-7s %-7s %s" %
   116                ("PCOMM", "PID", "PPID", "AGE(ms)", "ARGV"))
   117  
   118  buffer = None
   119  pidToArgv = defaultdict(list)
   120  
   121  def _print_event(cpu, data, size): # callback
   122      """Print the exit event."""
   123      global buffer
   124      e = buffer["events"].event(data)
   125  
   126      comm = e.comm.decode()
   127      if comm == "3":
   128          # For absolutely unknown reasons, 'crun' appears as '3'.
   129          comm = "crun"
   130  
   131      if e.isArgv:
   132          pidToArgv[e.pid].append(e.argv)
   133          return
   134  
   135      if comm not in ["podman", "crun", "runc", "conmon", "netavark", "aardvark-dns"]:
   136          try:
   137              del(pidToArgv[e.pid])
   138          except Exception:
   139              pass
   140          return
   141  
   142      age = (e.exit_time - e.start_time) / 1e6
   143      argv = b' '.join(pidToArgv[e.pid]).replace(b'\n', b'\\n')
   144      print("%-16s %-7d %-7d %-7.2f %s" %
   145                (comm, e.pid, e.ppid, age, argv.decode()), end="")
   146      print()
   147  
   148      try:
   149          del(pidToArgv[e.pid])
   150      except Exception:
   151          pass
   152  
   153  def snoop(bpf, event_handler):
   154      bpf["events"].open_perf_buffer(event_handler)
   155      while True:
   156          bpf.perf_buffer_poll()
   157  
   158  def main():
   159      global buffer
   160      try:
   161          buffer = BPF(text=bpf_src)
   162          execve_fnname = buffer.get_syscall_fnname("execve")
   163          buffer.attach_kprobe(event=execve_fnname, fn_name="syscall__execve")
   164          _print_header()
   165          snoop(buffer, _print_event)
   166      except KeyboardInterrupt:
   167          print()
   168          sys.exit()
   169  
   170      return 0
   171  
   172  if __name__ == '__main__':
   173      main()