13

I'm not going for complicated tools like AppArmor complain mode, I need easy tools to tell me which files are accessed by a specific program.

Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
Boll19
  • 167

3 Answers3

12

Per Chris Down, you can use strace -p to examine an already running process, to see what files it opens from now until the time you terminate strace or the process itself finishes.

If you want to see files opened for the entire duration of a process, right from the start, use strace with the executable name. Adding -f ensures that any forked sub-processes also get reported. Example

# strace -e open -f /bin/id
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libselinux.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libdl.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/proc/thread-self/attr/current", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/proc/self/task/1581/attr/current", O_RDONLY|O_CLOEXEC) = 3
open("/usr/share/locale/locale.alias", O_RDONLY|O_CLOEXEC) = 3
open("/usr/share/locale/en_US.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en_US/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.UTF-8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en.utf8/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/share/locale/en/LC_MESSAGES/coreutils.mo", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 3
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 3
open("/etc/passwd", O_RDONLY|O_CLOEXEC) = 3
open("/etc/group", O_RDONLY|O_CLOEXEC)  = 3
open("/etc/group", O_RDONLY|O_CLOEXEC)  = 3
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
+++ exited with 0 +++
#

Using lsof to see what files a process currently has open

# lsof -p $(pidof NetworkManager)
COMMAND   PID USER   FD      TYPE             DEVICE  SIZE/OFF     NODE NAME
NetworkMa 722 root  cwd       DIR              253,0       224       64 /
NetworkMa 722 root  rtd       DIR              253,0       224       64 /
NetworkMa 722 root  txt       REG              253,0   2618520   288243 /usr/sbin/NetworkManager
NetworkMa 722 root  mem       REG              253,0     27776    34560 /usr/lib64/libnss_dns-2.17.so
[...]
#

If you have SystemTap, you can monitor the entire host for files being opened.

[root@localhost tmp]# cat mon
#!/usr/bin/env stap
probe syscall.open { printf ("pid %d program %s opened %s\n", pid(), execname(), filename) }
# ./mon
pid 14813 program touch opened "/etc/ld.so.cache"
pid 14813 program touch opened "/lib64/libc.so.6"
pid 14813 program touch opened 0x7f7a8c6ec8d0
pid 14813 program touch opened "foo2"
[...]
#
steve
  • 21,892
  • 2
    open isn't the only relevant system call. For example it is possible to pass file descriptors between processes over a unix socket, and there is the openat system call which can also open a file. – kasperd Jan 27 '18 at 17:47
  • ---- SIGUSR1 {si_signo=SIGUSR1, si_code=SI_TKILL, si_pid=6026, si_uid=1002} ---- whats that – Boll19 Jan 28 '18 at 06:55
  • kaspers, do I only need to search for 'openat' at the strace output command? – Boll19 Jan 29 '18 at 05:33
  • Trying open a file (but the file may not be exist) is displayed in the 'strace' outputs too? – Boll19 Jan 29 '18 at 05:41
  • Boll19, files that fail to open due to them not existing are happily reported within strace, see the ENOENT lines in the example. – steve Feb 03 '18 at 13:44
5

You can use opensnoop from BCC, which uses eBPF under the hood:

# ./opensnoop -p 1576
PID    COMM      FD ERR PATH
1576   snmpd     11   0 /proc/sys/net/ipv6/conf/lo/forwarding
1576   snmpd     11   0 /proc/sys/net/ipv6/neigh/lo/base_reachable_time_ms
1576   snmpd      9   0 /proc/diskstats
1576   snmpd      9   0 /proc/stat
1576   snmpd      9   0 /proc/vmstat
[...]

This is quite performant since it uses kprobes instead of having to restart syscalls, like strace does.

You can also do this with strace (potentially with -f to follow the traced process' children), but its way of operating, involving restarting syscalls as part of ptrace will slow down your application somewhat:

# strace -e open -p 15735
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/gconv/gconv-modules.cache", O_RDONLY) = -1 ENOENT (No such file or directory)
open("/usr/lib/gconv/gconv-modules", O_RDONLY|O_CLOEXEC) = 3
open("/usr/lib/python2.7/site-packages", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 4
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
open("/etc/localtime", O_RDONLY|O_CLOEXEC) = 8
[...]

You can also start your application this way if desired, using strace [executable], or strace -f [executable].

Chris Down
  • 125,559
  • 25
  • 270
  • 266
5

My favorite tool for monitoring which files an application opens is the powerful monitoring framework sysdig.

For monitoring all the open files opened by a program named exe_file:

sudo sysdig -p "proc.name=exe_file %12user.name %6proc.pid %12proc.name %3fd.num %fd.typechar %fd.name" evt.type=open

Monitoring all the files opened in the server:

sudo sysdig -p "%12user.name %6proc.pid %12proc.name %3fd.num %fd.typechar %fd.name" evt.type=open

Creating a trace file that will only contain writing events in home directories (which we can inspect later with sysdig -r writetrace.scap.gz):

sudo sysdig -p "%user.name %proc.name %fd.name" "evt.type=write and fd.name contains /home/" -z -w writetrace.scap.gz

Seeing everything at syscall level a process named exe_file does:

sudo sysdig proc.name=exe_file

Sysdig has many chisels, see for more interesting things it can do:

You also have got dtrace that is not much used in Linux, but is still use a lot with *BSD operating systems:

# Files opened by process,
dtrace -n 'syscall::open*:entry { printf("%s %s",execname,copyinstr(arg0)); }'

Besides sysdig, strace and dtrace, you also have got ltrace, which records/intercepts signals/dynamic libraries/system calls which are called/received by a process:

ltrace is a program that simply runs the specified command until it exits. It intercepts and records the dynamic library calls which are called by the executed process and the signals which are received by that process. It can also intercept and print the system calls executed by the program.

$ltrace exe_file
_libc_start_main(0x400624, 1, 0x7ffcb7b6d7c8, 0x400710 <unfinished ...>  
time(0)                                                                              = 1508018406  
srand(0x59e288e6, 0x7ffcb7b6d7c8, 0x7ffcb7b6d7d8, 0)                                 = 0  
sprintf("mkdir -p -- '/opt/sms/AU/mo'", "mkdir -p -- '%s'", "/opt/sms/AU/mo")        = 28  
system("mkdir -p -- '/opt/sms/AU/mo'" <no return ...>  
--- SIGCHLD (Child exited) ---  
<... system resumed> )                                                               = 0  
rand(2, 0x7ffcb7b6d480, 0, 0x7f9d6d4622b0)                                           = 0x2d8ddbe1  
sprintf("/opt/sms/AU/mo/tmp.XXXXXX", "%s/tmp.XXXXXX", "/opt/sms/AU/mo")      = 29  
mkstemp(0x7ffcb7b6d5c0, 0x40080b, 0x40081a, 0x7ffffff1)                              = 3  
sprintf("/opt/sms/AU/mo/tmp.XXXXXX", "%s/tmp.XXXXXX", "/opt/sms/AU/mo")      = 29  
mkstemp(0x7ffcb7b6d5c0, 0x40080b, 0x40081a, 0x7ffffff1)                              = 4  
+++ exited (status 0) +++  

If the program is small, you might also consider disassembling it with objdump -d exe_file or disassembling/decompiling it with Hopper, to see all the files it deals with.

For more details see: Understanding what a Linux binary is doing

As a first approach, I would also do:

strings exe_file

It is a low cost approach, and with luck some of the files names might just be present in ASCII mode in the binary file with luck.

See also related answer Why are true and false so large?

If binaries/files that come with the distribution you can also fetch the sources from the sources repositories of the distribution, or the official repositories of the actual utility.

As a last resource, you can always use tools like gdb or rr to debug the binary in real time.

galoget
  • 349
Rui F Ribeiro
  • 56,709
  • 26
  • 150
  • 232
  • aaa43bb66:~ # sudo proc.name=exe_file sysdig -p "%12user.name %6proc.pid %12proc.name %3fd.num %fd.typechar %fd.name" evt.type=open Unable to load the driver error opening device /dev/sysdig0. Make sure you have root credentials and that the sysdig-probe module is loaded. – Boll19 Jan 29 '18 at 05:45
  • /
    aaa43bb66:~ # sudo proc.name=exe_file sysdig -p "%12user.name %6proc.pid %12proc.name %3fd.num %fd.typechar %fd.name" evt.type=open
        Unable to load the driver    error opening device /dev/sysdig0. Make sure you have root credentials and that the sysdig-probe module is loaded.
    /
    – Boll19 Jan 29 '18 at 05:53
  • @Boll19 Got an error there, corrected it. That message seems about a sysdig bug (are you using ARM?), please post a new question for it. – Rui F Ribeiro Jan 29 '18 at 09:51