Exploring Xorg connections
There is part two: Monitoring Raw X11 Communication or why Chromium opens 7 Xorg connections
Lately I have been playing around with raw X11 protocol by writing apps that work directly with Xorg by establishing a unix socket connection with it. It was pretty fun to play with and feels like easier than Xlib (but maybe I just got better). While doing all that I got interested with how other programs interact with Xorg and decided to explore how other programs do it and look at programs taht don’t have ‘perceptible’ windows but still maintain connections. Bellow is just my notes about that.
So when the system starts up on my setup there are 11 connections to the Xorg. These are when I have “zero” windows opens. Here is the list of apps that open connection with Xorg: xinit, lxsession, lxsession, openbox, lxpolkit, lxpanel, pcmanfm, lxclipboard, nm-applet, pulseaudio, at-spi2-registr.
Initially when I started with exploring Xorg and its connections I started with simple listing all unix connection with ss and then grepping for X11. This gave me a list of connections similar to the list bellow.
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
--------------------------------------------------------------------------------------
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 16834 * 16187
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 17760 * 16727
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 14971 * 16724
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 14980 * 18853
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 26903 * 16783
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 2709 * 16738
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 26836 * 24813
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 28684 * 15175
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 18866 * 16081
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 24848 * 13486
u_str ESTAB 0 0 @/tmp/.X11-unix/X0 18869 * 24841
It gives us a list of all connections opened on Xorg side with other applications in the system (minus 2 extra listening sockets). If we run ss with an extra option -p, it will give the name of the program that is sitting on one end of the connection. For example if we run ss -p | grep X11 it will show us that all of the previous 11 connections have users:((“Xorg”,pid=656,fd=43)) for the program part.
If we want to get a list of all the user programs that is connected to the to Xorg then we can get a list of all connections and then search for “Peer Port” section and relate a connection with Xorg manually. For the list above if I search for port 16187 in my list of connections I will find a single connection with 16187 as “Source Port”. In my case it is pulseaudio.
u_str ESTAB 0 0 * 16187 * 16834 users:(("pulseaudio",pid=752,fd=34))
It could be find to do this manually for a one of task but if you want to continuously explore different connections this task might become too daunting. To simplify my explorations I wrote a tiny and dirty python script to do this manual task automatically. It goes all over the lines and finds all X11 connections and then finds all programs that is sitting on the other side of the connections. After the script is run the output looks like this:
1 | 14971 - Xorg | pid:655 16724 - xinit
2 | 17760 - Xorg | pid:681 16727 - lxsession
3 | 26836 - Xorg | pid:681 24813 - lxsession
4 | 18869 - Xorg | pid:709 24841 - openbox
5 | 14980 - Xorg | pid:712 18853 - lxpolkit
6 | 18866 - Xorg | pid:713 16081 - lxpanel
7 | 18863 - Xorg | pid:715 25659 - pcmanfm
8 | 2709 - Xorg | pid:718 16738 - lxclipboard
9 | 24848 - Xorg | pid:723 13486 - nm-applet
10 | 16834 - Xorg | pid:752 16187 - pulseaudio
11 | 26903 - Xorg | pid:865 16783 - at-spi2-registr
From this list we can see that that is there a unix socket connection with one side being Xorg and port 16834 and the other side is pulseaudio port 16188 and pulseaudio has pid of 752.
Here is the script that used list of connected apps
#!/usr/bin/python
import sys
import os
import subprocess
all_data = {}
result = []
match_list = [
'/tmp/.X11-unix/X0',
'@/tmp/.X11-unix/X0',
]
def get_int(string):
try:
return int(string)
except:
return -1
def process_line(line):
parts = line.split()
if len(parts) != 10:
return
netid = parts[0]
state = parts[1]
recvq = parts[2]
sendq = parts[3]
local_address = parts[4]
local_port = get_int(parts[5])
remote_address = parts[6]
remote_port = get_int(parts[7])
program = parts[8]
name, pid = get_program(program)
all_data[local_port] = {
"netid" : netid,
"state" : state,
"recvq" : recvq,
"sendq" : sendq,
"local_address" : local_address,
"local_port" : local_port,
"remote_address" : remote_address,
"remote_port" : remote_port,
"program" : program,
"program_name" : name,
"program_pid" : pid,
}
if local_address in match_list:
result.append({
'local_port' : local_port,
'remote_port' : remote_port,
'local_program': program,
"program_name" : name,
"program_pid" : pid,
})
def get_program(string):
try:
parts = string.split(',')
name = parts[0].split("\"")[1]
pid = int(parts[1].split("=")[1])
except:
name = "[no name]"
pid = "[no pid]"
return name, pid
if __name__ == '__main__':
system_result = subprocess.run(["ss", "-p", "-u", "-x"], capture_output=True)
for line in system_result.stdout.split(b'\n'):
process_line(str(line))
if len(result) == 0:
print("Nothing found. Maybe you forgot to give '-p' flag to 'ss'?")
else:
for index, item in enumerate(result):
remote_program_str = all_data[item['remote_port']]['program']
name, pid = get_program(remote_program_str)
item['remote_program_name'] = name
item['remote_program_pid'] = pid
result = sorted(result, key=lambda x: x['remote_program_pid'])
for index, item in enumerate(result):
local_name = item['program_name']
local_pid = item['program_pid']
local_port = item['local_port']
remote_port = item['remote_port']
remote_name = item['remote_program_name']
remote_pid = item['remote_program_pid']
print("{:3}: | {:>9} - {} | pid:{:<9} {:>9} - {}".format(
index + 1,
local_port,
local_name,
remote_pid,
remote_port,
remote_name,
))
Programs that use multiple connections
At the very beginning (before I wrote that script) I noticed that programs open multiple connections to Xorg and that why I wrote a script to see what exactly is behind those connections. For example if you run skype it will open almost 8 connections to Xorg server, firefox opens 2 and telegram-desktop also opens 2 connections. From usage perspective 1 connection should be enough as you can do everything with it. But I guess these apps split them for some efficiency gains.