The pstree
command works similarly to ps
since it lists the current running processes in the Linux system but in a more graphical way. Is not a typical command, but it might be interesting to know about it since it is pretty descriptive and easy to use.
As you may presume by the name of the command, it shows the processes with a tree structure view in order to ease the understanding of what are the relations between the processes. Is not an easy parsable format for any kind of automation but more for troubleshooting and audit.
Continue reading to check some examples to learn more about this tool.
Listing the system processes in tree structure
To get an outcome with pstree
command just type it in your shell session without any option, and you’ll get all the child processes of systemd running on the system in a tree structure format:
$ pstree
Although the output might seem very descriptive as is, it lacks information like the PID, the full command line, the owner of the process and so on.
We can then use the option -a or –arguments which will display the command line used for each of the processes:
$ pstree -a
The above command will produce an output similar to the previous picture, but this time with more information about the process options and the arguments used:
systemd --switched-root --system --deserialize 22
├─NetworkManager --no-daemon
│ ├─dhclient -d -q -sf /usr/libexec/nm-dhcp-helper -pf /var/run/dhclient-eth0.pid -lf /var/lib/NetworkManager/dhclient-5fb06bd0-0bb0-7ffb-45f1-d6edd65f3e03-eth0.lease -cf /var/lib/NetworkManager/dhclient-eth0.conf eth0
│ └─2*[{NetworkManager}]
├─agetty --noclear tty1 linux
├─auditd
│ └─{auditd}
├─chronyd
├─crond -n
├─dbus-daemon --system --address=systemd: --nofork --nopidfile --systemd-activation
│ └─{dbus-daemon}
├─haveged -w 3072 -v 1 --Foreground
├─lvmetad -f
├─master -w
│ ├─pickup -l -t unix -u
│ └─qmgr -l -t unix -u
├─polkitd --no-debug
│ └─6*[{polkitd}]
├─rsyslogd -n
│ └─2*[{rsyslogd}]
├─sshd -D -u0
│ └─sshd
│ └─sshd
│ └─bash
│ └─sudo su fse
│ └─su fse
│ └─bash
│ └─pstree -a
├─systemd-journal
├─systemd-logind
├─systemd-udevd
└─tuned -Es /usr/sbin/tuned -l -P
└─4*[{tuned}]
Besides printing the full command line issued for the process, you can obtain the PID assigned for each of them by using -p or –show-pids option:
$ pstree -p
The output varies and it shows between “()” the PID for each of the processes. Note that in this output the repeated processes are expanded to assign a PID for each like polkitd or tuned. This is because the -p option implicitly use the -c to avoid collapsing similar processes in a single line:
systemd(1)─┬─NetworkManager(1989)─┬─dhclient(2003)
│ ├─{NetworkManager}(1992)
│ └─{NetworkManager}(1994)
├─agetty(658)
├─auditd(592)───{auditd}(593)
├─chronyd(632)
├─crond(652)
├─dbus-daemon(622)───{dbus-daemon}(637)
├─haveged(621)
├─lvmetad(477)
├─master(1080)─┬─pickup(3003)
│ └─qmgr(1082)
├─polkitd(618)─┬─{polkitd}(640)
│ ├─{polkitd}(641)
│ ├─{polkitd}(643)
│ ├─{polkitd}(647)
│ ├─{polkitd}(648)
│ └─{polkitd}(651)
├─rsyslogd(989)─┬─{rsyslogd}(993)
│ └─{rsyslogd}(994) ├─sshd(990)───sshd(2953)───sshd(2956)───bash(2957)───sudo(2979)───su(2981)───bash(2982)───pstree(3062)
├─systemd-journal(460)
├─systemd-logind(639)
├─systemd-udevd(490)
└─tuned(988)─┬─{tuned}(1025)
├─{tuned}(1026)
├─{tuned}(1029)
└─{tuned}(1031)
You can leverage the previous result by adding the option -n or –numeric-sort which will order the output from the lowest PID to the highest:
$ pstree -np
systemd(1)─┬─systemd-journal(460)
├─lvmetad(477)
├─systemd-udevd(490)
├─auditd(592)───{auditd}(593)
├─polkitd(618)─┬─{polkitd}(640)
│ ├─{polkitd}(641)
│ ├─{polkitd}(643)
│ ├─{polkitd}(647)
│ ├─{polkitd}(648)
│ └─{polkitd}(651)
├─haveged(621)
├─dbus-daemon(622)───{dbus-daemon}(637)
├─chronyd(632)
├─systemd-logind(639)
├─crond(652)
├─agetty(658)
├─tuned(988)─┬─{tuned}(1025)
│ ├─{tuned}(1026)
│ ├─{tuned}(1029)
│ └─{tuned}(1031)
├─rsyslogd(989)─┬─{rsyslogd}(993)
│ └─{rsyslogd}(994)
├─sshd(990)───sshd(2953)───sshd(2956)───bash(2957)───sudo(2979)───su(2981)───bash(2982)───pstree(3130)
├─master(1080)─┬─qmgr(1082)
│ └─pickup(3003)
├─NetworkManager(1989)─┬─{NetworkManager}(1992)
│ ├─{NetworkManager}(1994)
│ └─dhclient(2003)
└─anacron(3081)
Another interesting option that could be helpful is the -Z or –security-context if pstree
was compiled with SELinux support:
$ pstree -Z
This will show for each running process the SELinux security context:
systemd(`system_u:system_r:init_t:s0')
├─NetworkManager(`system_u:system_r:NetworkManager_t:s0')
│ ├─dhclient(`system_u:system_r:dhcpc_t:s0')
│ └─2*[{NetworkManager}(`system_u:system_r:NetworkManager_t:s0')]
├─agetty(`system_u:system_r:getty_t:s0-s0:c0.c1023')
├─anacron(`system_u:system_r:system_cronjob_t:s0-s0:c0.c1023')
├─auditd(`system_u:system_r:auditd_t:s0')
│ └─{auditd}(`system_u:system_r:auditd_t:s0')
├─chronyd(`system_u:system_r:chronyd_t:s0')
├─crond(`system_u:system_r:crond_t:s0-s0:c0.c1023')
├─dbus-daemon(`system_u:system_r:system_dbusd_t:s0-s0:c0.c1023')
│ └─{dbus-daemon}(`system_u:system_r:system_dbusd_t:s0-s0:c0.c1023')
├─haveged(`system_u:system_r:entropyd_t:s0')
├─lvmetad(`system_u:system_r:lvm_t:s0')
├─master(`system_u:system_r:postfix_master_t:s0')
│ ├─pickup(`system_u:system_r:postfix_pickup_t:s0')
│ └─qmgr(`system_u:system_r:postfix_qmgr_t:s0')
├─polkitd(`system_u:system_r:policykit_t:s0')
│ └─6*[{polkitd}(`system_u:system_r:policykit_t:s0')]
├─rsyslogd(`system_u:system_r:syslogd_t:s0')
│ └─2*[{rsyslogd}(`system_u:system_r:syslogd_t:s0')]
├─sshd(`system_u:system_r:sshd_t:s0-s0:c0.c1023')
│ └─sshd(`system_u:system_r:sshd_t:s0-s0:c0.c1023')
│ └─sshd(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
│ └─bash(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
│ └─sudo(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
│ └─su(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
│ └─bash(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
│ └─pstree(`unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023')
├─systemd-journal(`system_u:system_r:syslogd_t:s0')
├─systemd-logind(`system_u:system_r:systemd_logind_t:s0')
├─systemd-udevd(`system_u:system_r:udev_t:s0-s0:c0.c1023')
└─tuned(`system_u:system_r:tuned_t:s0')
└─4*[{tuned}(`system_u:system_r:tuned_t:s0')]
You may pass to pstree
a PID or user to get a specific process siblings or the processes executed by the user respectively:
$ pstree dbus
$ pstree 1989
Understanding the pstree output
The root process is displayed always the first without any special character in the beginning, while any child processes have indentation and starts with a particular character that graphically shows its parent process.
Another aspect to interpret from the outcome are the collapsed processes. If there are multiple processes with the same name then pstree
, as long as the -c is not used, will group them and tell how many of them are running with a number, “*” symbol and the repeated process name surrounded by brackets “[]”:
For those commands run by threads and not as a process, the command name will be surrounded by brackets “{}”:
Similarly, to the repeated processes, multiple threads running a same binary will be grouped in the same way as the processes like NetworkManager in the following picture:
To wrap up
The pstree
is a simple and visual command, that might be interesting to look for any anomalies or suspicious activities if any quickly. Therefore, if you find any bash commands under a httpd
process, and the service is not expected to run any then, it should be a red flag in terms of security.