1#!/usr/bin/env python 2# 3# bashreadline Print entered bash commands from all running shells. 4# For Linux, uses BCC, eBPF. Embedded C. 5# 6# USAGE: bashreadline [-s SHARED] 7# This works by tracing the readline() function using a uretprobe (uprobes). 8# When you failed to run the script directly with error: 9# `Exception: could not determine address of symbol b'readline'`, 10# you may need specify the location of libreadline.so library 11# with `-s` option. 12# 13# Copyright 2016 Netflix, Inc. 14# Licensed under the Apache License, Version 2.0 (the "License") 15# 16# 28-Jan-2016 Brendan Gregg Created this. 17# 12-Feb-2016 Allan McAleavy migrated to BPF_PERF_OUTPUT 18 19from __future__ import print_function 20from bcc import BPF 21from time import strftime 22import argparse 23 24parser = argparse.ArgumentParser( 25 description="Print entered bash commands from all running shells", 26 formatter_class=argparse.RawDescriptionHelpFormatter) 27parser.add_argument("-s", "--shared", nargs="?", 28 const="/lib/libreadline.so", type=str, 29 help="specify the location of libreadline.so library.\ 30 Default is /lib/libreadline.so") 31args = parser.parse_args() 32 33name = args.shared if args.shared else "/bin/bash" 34 35# load BPF program 36bpf_text = """ 37#include <uapi/linux/ptrace.h> 38#include <linux/sched.h> 39 40struct str_t { 41 u32 pid; 42 char str[80]; 43}; 44 45BPF_PERF_OUTPUT(events); 46 47int printret(struct pt_regs *ctx) { 48 struct str_t data = {}; 49 char comm[TASK_COMM_LEN] = {}; 50 if (!PT_REGS_RC(ctx)) 51 return 0; 52 data.pid = bpf_get_current_pid_tgid() >> 32; 53 bpf_probe_read_user(&data.str, sizeof(data.str), (void *)PT_REGS_RC(ctx)); 54 55 bpf_get_current_comm(&comm, sizeof(comm)); 56 if (comm[0] == 'b' && comm[1] == 'a' && comm[2] == 's' && comm[3] == 'h' && comm[4] == 0 ) { 57 events.perf_submit(ctx,&data,sizeof(data)); 58 } 59 60 61 return 0; 62}; 63""" 64 65b = BPF(text=bpf_text) 66b.attach_uretprobe(name=name, sym="readline", fn_name="printret") 67 68# header 69print("%-9s %-7s %s" % ("TIME", "PID", "COMMAND")) 70 71def print_event(cpu, data, size): 72 event = b["events"].event(data) 73 print("%-9s %-7d %s" % (strftime("%H:%M:%S"), event.pid, 74 event.str.decode('utf-8', 'replace'))) 75 76b["events"].open_perf_buffer(print_event) 77while 1: 78 try: 79 b.perf_buffer_poll() 80 except KeyboardInterrupt: 81 exit() 82