Execsnoop cli args matching (#1115)

* adding args matching option

* fixing typos

* fixing merge artefacts

* [execsnoop]: adding documentation on -l in man

* [execsnoop][man]: fixing typo in commands name
diff --git a/man/man8/execsnoop.8 b/man/man8/execsnoop.8
index 5f700cc..488356e 100644
--- a/man/man8/execsnoop.8
+++ b/man/man8/execsnoop.8
@@ -2,7 +2,7 @@
 .SH NAME
 execsnoop \- Trace new processes via exec() syscalls. Uses Linux eBPF/bcc.
 .SH SYNOPSIS
-.B execsnoop [\-h] [\-t] [\-x] [\-n NAME]
+.B execsnoop [\-h] [\-t] [\-x] [\-n NAME] [\-l LINE]
 .SH DESCRIPTION
 execsnoop traces new processes, showing the filename executed and argument
 list.
@@ -31,7 +31,10 @@
 Include failed exec()s
 .TP
 \-n NAME
-Only print command lines matching this name (regex), matched anywhere
+Only print command lines matching this name (regex)
+.TP
+\-l LINE
+Only print commands where arg contains this line (regex)
 .SH EXAMPLES
 .TP
 Trace all exec() syscalls:
@@ -46,9 +49,13 @@
 #
 .B execsnoop \-x
 .TP
-Only trace exec()s where the filename or arguments contain "mount":
+Only trace exec()s where the filename contains "mount":
 #
-.B opensnoop \-n mount
+.B execsnoop \-n mount
+.TP
+Only trace exec()s where argument's line contains "testpkg":
+#
+.B execsnoop \-l testpkg
 .SH FIELDS
 .TP
 TIME(s)
diff --git a/tools/execsnoop.py b/tools/execsnoop.py
index 22cb54d..fc086f7 100755
--- a/tools/execsnoop.py
+++ b/tools/execsnoop.py
@@ -30,6 +30,7 @@
     ./execsnoop -x        # include failed exec()s
     ./execsnoop -t        # include timestamps
     ./execsnoop -n main   # only print command lines containing "main"
+    ./execsnoop -l tpkg   # only print command where arguments contains "tpkg" 
 """
 parser = argparse.ArgumentParser(
     description="Trace exec() syscalls",
@@ -41,6 +42,8 @@
     help="include failed exec()s")
 parser.add_argument("-n", "--name",
     help="only print commands matching this name (regex), any arg")
+parser.add_argument("-l", "--line",
+    help="only print commands where arg contains this line (regex)")
 args = parser.parse_args()
 
 # define BPF program
@@ -192,6 +195,9 @@
             skip = True
         if args.name and not re.search(args.name, event.comm):
             skip = True
+        if args.line and not re.search(args.line,
+                                       b' '.join(argv[event.pid]).decode()):
+            skip = True
 
         if not skip:
             if args.timestamp:
@@ -200,8 +206,11 @@
             print("%-16s %-6s %-6s %3s %s" % (event.comm.decode(), event.pid,
                     ppid if ppid > 0 else "?", event.retval,
                     b' '.join(argv[event.pid]).decode()))
+        try:
+            del(argv[event.pid])
+        except Exception:
+            pass
 
-        del(argv[event.pid])
 
 # loop with callback to print_event
 b["events"].open_perf_buffer(print_event)
diff --git a/tools/execsnoop_example.txt b/tools/execsnoop_example.txt
index 02840a4..a538165 100644
--- a/tools/execsnoop_example.txt
+++ b/tools/execsnoop_example.txt
@@ -52,13 +52,29 @@
 
 
 A -t option can be used to include a timestamp column, and a -n option to match
-on a name or substring from the full command line (filename + args). Regular
-expressions are allowed. For example, matching commands containing "mount":
+on a name. Regular expressions are allowed. 
+For example, matching commands containing "mount":
 
 # ./execsnoop -tn mount
 TIME(s) PCOMM            PID    RET ARGS
-2.849   bash             18049    0 /bin/mount -p
+2.849   mount            18049    0 /bin/mount -p
 
+The -l option can be used to only show command where one of the arguments
+matches specified line. The limitation is that we are looking only into first 20
+arguments of the command. For example, matching all command where one of the argument
+is "testpkg":
+
+# ./execsnoop.py -l testpkg
+PCOMM            PID    PPID   RET ARGS
+service          3344535 4146419   0 /usr/sbin/service testpkg status
+systemctl        3344535 4146419   0 /bin/systemctl status testpkg.service
+yum              3344856 4146419   0 /usr/local/bin/yum remove testpkg
+python           3344856 4146419   0 /usr/local/bin/python /usr/local/bin/yum remove testpkg
+yum              3344856 4146419   0 /usr/bin/yum remove testpkg
+yum              3345086 4146419   0 /usr/local/bin/yum install testpkg
+python           3345086 4146419   0 /usr/local/bin/python /usr/local/bin/yum install testpkg
+yum              3345086 4146419   0 /usr/bin/yum install testpkg
+rpm              3345452 4146419   0 /bin/rpm -qa testpkg
 
 USAGE message:
 
@@ -73,9 +89,12 @@
   -x, --fails           include failed exec()s
   -n NAME, --name NAME  only print commands matching this name (regex), any
                         arg
+  -l LINE, --line LINE  only print commands where arg contains this line
+                        (regex)
 
 examples:
     ./execsnoop           # trace all exec() syscalls
     ./execsnoop -x        # include failed exec()s 
     ./execsnoop -t        # include timestamps
     ./execsnoop -n main   # only print command lines containing "main"
+    ./execsnoop -l tpkg   # only print command where arguments contains "tpkg"