You are not logged in.

#1 2014-11-24 15:05:57

blackle
Member
Registered: 2013-06-11
Posts: 8

Maximum Clients Reached + Switching between two X servers

Hello.

Recently I've been getting a lot of "maximum number of x clients reached" errors. On my computer I run two X servers. I switch between them pretty regularly with crtl+alt+f1/f2.

I've been trying to figure out why I keep getting this error. xlsclients only lists 12 items, xrestop doesn't show much more. As of this writing I'm currently getting this error and cannot open any more processes. `lsof -U` lists 203 items (mostly chromium, but I don't think that's the problem here), but `sudo ls /proc/2541/fd` (2541 is the pid of Xorg.bin) has 259 items.

Now here's the thing, if I switch between my two x servers with ctrl+alt+f1/f2 (this works even if tty2 is a framebuffer) the number of items in /proc/2541/fd increases by one. Indeed if I run `sudo ls -p 2541` there's a new entry, /dev/dri/card0

I think that every time I switch, X re-opens my graphics card, but never closes the old connection. the number of items lsof -U doesn't change when I do this, so none of my programs are to blame, this is just X.

Because there are a lot of old connections floating around, I'm guessing the number of clients X can have is severely limited. Is this a bug? How do I fix this? How do I report this?

update: filed a bug on the xorg bugtracker. Not sure if there is anything I can do in the meantime other than restart X when things get bad and minimize the amount of switching I do

Thanks!

Last edited by blackle (2014-11-24 15:43:22)

Offline

#2 2016-02-12 03:49:18

ccgoo
Member
Registered: 2016-02-12
Posts: 10

Re: Maximum Clients Reached + Switching between two X servers

Sorry for the late reply.

I encountered the exactly same problem as you at the beginning of last year, and found a way to resolve it after researching a couple of days. I saw this post of yours at the same time. That is why I say "sorry for the late reply" (I was supposed to answer you in last February or March). I don't know if you still have this problem. If so, I think it would help.

Since the way I start X is through command "startx", I write $HOME/.xserverrc of my own to customize how to start X server. $HOME/.xserverrc overrides the system default one.
To resolve this problem, I need to do something before start X. That is why I write a custom .xserverrc.  The $HOME/.xserverrc is the right one for this, I can write anything I want to do before start X in it.

Because I write a lot of comments about what I did and why I did it. So I think I'd better paste all content of the file here. Please read section "ABOUT" in the script to find out the reason of the problem, and the way how I resolve it.
The script looks like very long, containing a lot of lines, but in fact, most are comments, so don't be scared. The code is very simple. And actually, you don't have to read code, the section "ABOUT" is enough if you just wanna know the principle.

#!/bin/bash
# $HOME/.xserverrc

# ABOUT
# --------
# I create this file to override system default one in order to get rid of
# control from systemd. Why do I need to do this? Simply, systemd-logind
# will open /dev/dri/card0 every time I switch X out and back (ctrl-alt-Fx)
# without close previous fd. The opened fd is more and more till it is not
# able to open any more, which results in cannot launch any X applications.
#
# How does this happen? In a systemd-enabled Linux Distribution, every time
# we login, PAM module pam_systemd will register the login session into
# systemd-logind by starting systemd-logind.service.
# Systemd-logind manages the session, e.g. keep track of user state and
# their processes by the feature called CGROUP, offered by Linux kernel.
# It creates a new cgroup under "systemd" controller(subsystem) for this
# session, put the login shell in this cgroup so that every child process
# of the shell, child of child..., all will belong to this cgroup
# automatically. By watching this session cgroup, systemd-logind controls
# the session.
#
# So the X server launched in the login shell also belongs to the session
# cgroup, which means systemd-logind is aware of it too. Every time I switch
# X out and back, X server suspends and resumes, which makes systemd-logind
# re-open the main IO device file of X, /dev/dri/card0, always, as some
# unknown reason (I think it's bug of systemd-logind).
#
# Whatever. Why systemd-logind re-open /dev/dri/card0 always isn't important
# to me. I don't care. System-logind does it wrong, so I won't let it do it
# any more. I move the X server process out of that session cgroup, into
# another cgroup, so that systemd-logind won't see X server any more, which
# stops it from always re-opening file.
#
# In fact, I don't change cgroup of X server process directly, instead I
# change cgroup of process of this shell script, and run X server then
# (As child process, X server is not in that session cgroup either).
# 
# Epilogue of the "ABOUT"
# 1.
# X server usually needs root rights to run, that is why in most system, you
# can see X server binary file is "setuid". In Arch Linux Distribution, it is
# not because as mentioned above, X server is supposed to run in a session
# cgroup, which is controlled by systemd-logind, that means systemd-logind
# confines all X server can do to the session only, so X server process don't
# need root rights (e.g. it writes log file into $HOME/.local/share/xorg/
# instead of the system path /var/log/). But since I move X server out of the
# session cgroup, it needs root rights again. So you'll see some code below
# is nothing to do about CGROUP, it is only used for getting root rights back
# (Actually, a simple way to get root rights back is set "setuid" to X server
# binary file).
#
# 2.
# The session cgroup mentioned above, created by systemd-logind is something
# like this:
#       /sys/fs/cgroup/systemd/user.slice/user-1000.slice/session-c4.scope/
# The another cgroup into which I move X server process is:
#       /sys/fs/cgroup/systemd/user.slice/user-1000.slice/user@1000.service/
# When X server in the session cgroup, you can find log like this in Xorg.log
#       (II) systemd-logind: took control of session /org/freedesktop/login1/session/c3
#       (II) xfree86: Adding drm device (/dev/dri/card0)
#       (II) systemd-logind: got fd for /dev/dri/card0 226:0 fd 8 paused 0
# After I change its cgroup, you can see log like this:
#       (EE) systemd-logind: failed to get session: PID 2199 does not belong to any known session
#       (II) xfree86: Adding drm device (/dev/dri/card0)

set -e

# X(symlink) -> Xorg(sh)
# Xorg(sh) -> Xorg.wrap(bin)
# Xorg.wrap(bin) -> Xorg.bin(bin)
server=/usr/bin/X

#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# THIS BLOCK OF CODE AND COMMENTS ARE ALL FOR LETTING X GAIN ROOT RIGHTS.
# IT LOOKS LIKE SO MANY, BUT NOT THE HEART OF THIS SCRIPT.
#

xwrap_config_line='needs_root_rights=yes'
xwrap_config=/etc/X11/Xwrapper.config

# Use basic RE, not extended RE, because ed support basic RE only, and
# vim use basic RE too.
regex_line_orig="^#\($xwrap_config_line\)$"
regex_line_new="^\($xwrap_config_line\)$"
cmd_s_to_new="s/$regex_line_orig/\1/"
cmd_s_to_orig="s/$regex_line_new/#\1/"

# Because I have write permission to file /etc/X11/Xwrapper.config only,
# I don't have write permission to directory /etc/X11/, so I cannot use
# "sed -i" to change this file, like below
#----------------------------------------------------------------------
#sed -i "$cmd_s_to_new" $xwrap_config
#----------------------------------------------------------------------
# which is same as "create a new temp file and then move it to /etc/X11/".
# So I have to use alternative to change it. There are 4 ways, vim, ed,
# cp and >(redirect). All are change file-self but directory.
#
# 1. vim
#----------------------------------------------------------------------
#cat > /tmp/.vimscript <<eof
#":%$cmd_s_to_new
#:wq
#eof
#vim -s /tmp/.vimscript $xwrap_config
#----------------------------------------------------------------------
# Shortcomming of this method is I must create a temp vim script file, 
# I can not use stdin to input script into vim.
#
# 2. ed
#----------------------------------------------------------------------
#ed -s $xwrap_config <<eof
#1,$$cmd_s_to_new
#w
#q
#----------------------------------------------------------------------
#
# 3. cp
#----------------------------------------------------------------------
#sed "$cmd_s_to_new" $xwrap_config > /tmp/.sed_output
#cp /tmp/.sed_output $xwrap_config
#----------------------------------------------------------------------
#
# 4. >(redirect)
#----------------------------------------------------------------------
#cp $xwrap_config /tmp/.copy_tempfile
#sed "$cmd_s_to_new" /tmp/.copy_tempfile > $xwrap_config
#----------------------------------------------------------------------
#
# So in this situation, we have to use cp and sed together.
# Anyway, I select the 3rd way to do so.
#sed "$cmd_s_to_new" $xwrap_config > /tmp/.sed_output

#
#+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

enable_root_right()
{
        # Modify Xwrap.config to do setuid
        sed "$cmd_s_to_new" $xwrap_config > /tmp/.sed_output
        cp /tmp/.sed_output $xwrap_config
}

disable_root_right()
{
        sed "$cmd_s_to_orig" $xwrap_config > /tmp/.sed_output
        cp /tmp/.sed_output $xwrap_config
}

do_before_xserver_start()
{
        enable_root_right

        # Remove current shell and its children from current systemd session.
        # This is the most important thing I want to do in this script, this
        # is also why I write this script instead of use default /usr/bin/X.
        echo $$ > /sys/fs/cgroup/systemd/user.slice/user-`id -u`.slice/user@`id -u`.service/tasks
}

do_after_xserver_start()
{
        disable_root_right
}

start_xserver()
{
        $server "$@" &
        sleep 1 # option. in case "do_after_xserver_start" prior to xserver start
        do_after_xserver_start
        wait #$!
}

# Redefine this function to override the above one, because must use `exec' to run xserver.
start_xserver()
{
        (
                (
                        sleep 1 # must. ensure "do_after_xserver_start" after xserver start
                        do_after_xserver_start
                )&
        )
        exec $server "$@"
}

do_before_xserver_start
start_xserver "$@"

One more thing, if you are gonna to use this script, you need one more file, /etc/X11/Xwrapper.config , and it needs one line like this

#needs_root_rights=yes

(If you have your own Xwrapper.config already, add this line into it. If you have your own Xwrapper.config already, and you also have this line without the preceding '#' already, which means you or somebody added(enabled) it before explicitly, and want it to be preserved, then you will need to change above script a little bit, remove two functions which are enable_root_right() and disable_root_right().)

Last edited by ccgoo (2016-02-16 20:19:46)

Offline

#3 2016-02-12 05:12:17

x33a
Forum Fellow
Registered: 2009-08-15
Posts: 4,587

Re: Maximum Clients Reached + Switching between two X servers

ccgoo wrote:

One more thing, if you are gonna to use this script, you need one more file, /etc/X11/Xwrapper.config , and its content is like below (just one line)

#needs_root_rights=yes

Umm.. what is that line supposed to do? It will have no effect.

From "man Xwrapper.config":

Lines starting  with  a  #  in  Xwrapper.config are considered comments and will be ignored.

Perhaps you meant that If "needs_root_rights" is set to yes, then it should be commented?

Offline

#4 2016-02-12 19:55:42

ccgoo
Member
Registered: 2016-02-12
Posts: 10

Re: Maximum Clients Reached + Switching between two X servers

x33a wrote:

Umm.. what is that line supposed to do? It will have no effect.

Sorry, maybe I didn't make myself clear in section "ABOUT".

Well, in fact, I do want it has no effect, in usual times. As you know, by default, in Arch Linux, X server runs without root rights, so I want to preserve the default behavior.
I want this line to be enabled just when I start X through my custom .xserverrc, AND after X started, this line is gonna be disabled(commented) back to default. (See that script, there are two functions named do_before_xserver_start() and do_after_xserver_start().)
By doing so, it has no effect if X is started in other ways or by other users.

(I believe somebody is gonna say why I don't just keep Xwrapper.conf absent in usual times, and then create a one with that line (enabled) when I am using that custom .xserverrc to start X, and delete it after X started.    Yes, it's OK as well, and sounds simpler. But what if you do have a "Xwrapper.conf" of your own, and you don't want it to be deleted?)

Last edited by ccgoo (2016-02-16 19:48:24)

Offline

#5 2016-02-13 12:13:24

x33a
Forum Fellow
Registered: 2009-08-15
Posts: 4,587

Re: Maximum Clients Reached + Switching between two X servers

@ccgoo,

I didn't read your script at all tongue. Your explanation clarifies the rationale.

Offline

Board footer

Powered by FluxBB