Configure default session for GDM

Hi,

My current desktop setup is based on Rocky Linux 8 with KDE Plasma from EPEL. I’m using GDM as login manager instead of SDDM, because the latter seems to have some stability issues (random freezes on logout).

GDM seems to work nicely so far, except I have a little problem to solve. In the default setup, GDM allows the following sessions:

  • Fluxbox (I always install this to test video card configuration)
  • Standard (Wayland)
  • Custom
  • Standard (X11)
  • Plasma (X11)
  • User script

Is there any way I can configure GDM to only show the Plasma (X11) session while hiding all the others?

Alternatively, is there a way to make Plasma (X11) the default session for every user?

Here’s the problem. While GDM works fine, there’s no way to install it without fetching the whole GNOME kitchen sink and making GNOME the default session. Now I’m planning to install this in our local school, with about 100 users (and centralized authentication). What I want to avoid is having to tell every single user “choose Plasma X11 by clicking on the little thingy and before hitting Enter”. This has to be foolproof.

As far as I can tell, GDM configuration happens in /etc/gdm/custom.conf. There’s already a commented option WaylandEnable=false which conveniently disables the Wayland session if uncommented. Now if there was a way to disable all the other stuff except Plasma (or at least make Plasma the default for everybody) this would come in very handy.

Any suggestions ?

In similar vein, I had no desire for the “Gnome Classic” – already in el7. I did found out that that particular desktop is offered by package gnome-classic-session, so I exclude that from my package lists.
What does it have?

$ dnf -q rq -l gnome-classic-session
/usr/share/glib-2.0/schemas/00_org.gnome.shell.extensions.classic.gschema.override
/usr/share/gnome-session/sessions/gnome-classic.session
/usr/share/gnome-shell/modes/classic.json
/usr/share/gnome-shell/theme/calendar-today.svg
/usr/share/gnome-shell/theme/classic-process-working.svg
/usr/share/gnome-shell/theme/classic-toggle-off-intl.svg
/usr/share/gnome-shell/theme/classic-toggle-off-us.svg
/usr/share/gnome-shell/theme/classic-toggle-on-intl.svg
/usr/share/gnome-shell/theme/classic-toggle-on-us.svg
/usr/share/gnome-shell/theme/gnome-classic-high-contrast.css
/usr/share/gnome-shell/theme/gnome-classic.css
/usr/share/wayland-sessions/gnome-classic-wayland.desktop
/usr/share/xsessions/gnome-classic.desktop

It is actually the *.desktop files that are used in the menu.

Now, ask yourself, who has them and/or which ones you have installed:

$ dnf provides /usr/share/\*sessions/\*.desktop
$ rpm -qf /usr/share/*sessions/*.desktop

The trivial solution should be to remove the packages (and hence files) that you don’t want. For Gnome you probably can do that. For Fluxbox, … after card “works”?

What is left on the menu after those are gone?

1 Like

Thanks very much ! I guess I found an improvement to what you suggest.

I’m already using custom menu entries for my desktop configuration. Check out my setup.sh script around line 760:

The trick here is to catch two birds with one stone and to replace *.desktop files in /usr/share/xsession as well as /usr/share/wayland-session. Don’t want a session to show up? Simply use NoDisplay=true (and run update-desktop-database).

Works like a charm.

2 Likes

Are those *.desktop -files proper “config-files”, i.e. what happens if package gets an update?

When I run an update, I always run the script to replace the stock menu files by the custom versions. I know this may sound strange, but this has worked perfectly for the last decade or so.

Actually, it doesn’t. I too have shifted to use a “script”, although it’s called “Ansible playbook”. Updates and ensures that all config is correct. Could be run nightly …

2 Likes

Ansible is one of the technologies I have yet to wrap my head around. Just recently I started a Udemy course to learn it, but I guess it will take some time.

One more general question here. I’m using this bone-headed shell script to handle my post-install configuration:

Is Ansible a good replacement for this? Is it trivial to do with Ansible everything that this script does?

You can set the default session to use by copying the files in /usr/share/accountsservice/user-templates/ to /etc/accountsservice/user-templates/ and updating them to use whatever default session you want via the ‘Session=’ line

Some of the script are definitely trivial with Ansible.
Some parts of the script I’m not sure if I get what it does.

Here are some ideas, for starters:

configure_repos.yml

# Ansible can create *.repo files.  Sample data here is all wrong.
- hosts: all
  vars:
    use_epel: true
    use_rpmfusionfree: false
    use_rpmfusion: false
    use_irods: true
    thirdparty_repositories:
    - id:          "epel"
      name:        "{{ epel_repository }}"
      description: "Extra Packages for Enterprise Linux $releasever - $basearch"
      metalink:    "https://mirrors.fedoraproject.org/metalink?repo=epel-$releasever&arch=$basearch&infra=$infra&content=$contentdir"
      failovermethod: priority
      gpgkey:      file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-$releasever
      exclude:     ['ansible', 'ansible-doc', 'slurm*']
    - id:          "rpmfusionfree"
      depends:     [ "{{ epel_repository }}" ]
      name: "rpmfusion-free-updates"
      description: RPM Fusion for EL 7 - Free - Updates
      mirrorlist: "http://mirrors.rpmfusion.org/mirrorlist?repo=free-el-updates-released-7&arch=$basearch"
      gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-free-el-7
    - id:          "rpmfusion"
      name: "rpmfusion-nonfree-updates"
      description: RPM Fusion for EL 7 - Nonfree - Updates
      mirrorlist: "http://mirrors.rpmfusion.org/mirrorlist?repo=nonfree-el-updates-released-7&arch=$basearch"
      gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-rpmfusion-nonfree-el-7
      exclude: ['kmod-nvidia*']
    - id:          "irods"
      name:        renci-irods
      description: RENCI iRODS Repository
      baseurl: https://packages.irods.org/yum/pool/el$releasever/$basearch
      gpgkey:  https://packages.irods.org/irods-signing-key.asc
      metadata_expire: 1d

  tasks:
  - name: Define third-party repositories
    ansible.builtin.yum_repository:
      name:            "{{ item.name }}"
      file:            "{{ item.file | default(omit) }}"
      description:     "{{ item.description }}"
      mirrorlist:      "{{ item.mirrorlist | default(omit) }}"
      baseurl:         "{{ item.baseurl | default(omit) }}"
      metalink:        "{{ item.metalink | default(omit) }}"
      cost    :        "{{ item.cost | default(omit) }}"
      failovermethod:  "{{ item.failovermethod | default(omit) }}"
      gpgkey:          "{{ item.gpgkey | default(omit) }}"
      metadata_expire: "{{ item.metadata_expire | default(omit) }}"
      exclude:         "{{ item.exclude | default(omit) }}"
      gpgcheck:        "{{ item.gpgcheck | default(true) }}"
      repo_gpgcheck:   "{{ item.repo_gpgcheck | default(omit) }}"
      enabled:         "{{ item.enabled | default(false) }}"
      skip_if_unavailable: "{{ item.skip_if_unavailable | default(omit) }}"
    when: lookup('vars', 'use_' + item.id)
    loop: "{{ thirdparty_repositories }}"
    loop_control:
      label: "{{ item.name }}"

update_system.yml

- hosts: all
  tasks:
  - name: Check support status
    ansible.builtin.fail:
      msg: We fail here because major is not 8
    when: not ansible_facts['distribution_major_version'] is version( '8', '==' )

  - name: Ensure we have drpm
    ansible.builtin.dnf:
      name: 'drpm'
      state: latest
      update_cache: yes

  - name: Update all, except kernel
    ansible.builtin.dnf:
      name: '*'
      state: latest
      exclude: 'vdo,kmod-kvdo,kernel*'

  - name: Update kernel
    ansible.builtin.dnf:
      name: '*'
      state: latest

I like to ensure that files that potentially get copied to initramfs when kernel installs are certainly the latest version. Perhaps dnf is smart enough, but I rather not assume.

software.yml

- hosts: all
  tasks:
  - name: Install tools
    ansible.builtin.dnf:
      name: "{{ packages_tools }}"
      state: present
    tags:
    - install_tools

  - name: Install basic
    block:
    - name: Install basic
      vars:
        packages_basic:
        - '@base-x'
        - '@fonts'
        - xorg-x11-fonts-*    # can't remember whether the wildcard is ok
        - '@kde-desktop-environment'
      ansible.builtin.dnf:
        name: "{{ packages_basic }}"
        state: present
    # Ansible can run commands, although that is not preferred
    - name: Setting boot default to graphical environment.
      command: systemctl set-default graphical.target
    tags:
    - install_basic

  - name: Remove cruft
    ansible.builtin.dnf:
      name: "{{ packages_cruft }}"
      state: absent
    tags:
    - remove_cruft

  # This task runs only if tag is explicitly given
  - name: Install DVD/RW-related applications
    ansible.builtin.dnf:
      name: "{{ packages_dvdrw }}"
      state: present
    tags:
    - install_dvdrw
    - never

The variables packages_tools and packages_cruft have to be defined somewhere.
Could be in the tasks but more comfy in inventory files.

setup.yml

# Top level play that calls the other plays
- ansible.builtin.import_playbook: configure_repos.yml
- ansible.builtin.import_playbook: software.yml
- ansible.builtin.import_playbook: update_system.yml
1 Like

Wow. Thanks very much for that detailed answer. As soon as I can find some time, I will definitely invest it to learn Ansible.

The ansible-core includes command ansible-doc that shows documentation.
For example:

ansible-doc -l   # list available plugins
ansible-doc dnf  # show doc for ansible.builtin.dnf
ansible-doc copy # show doc for ansible.builtin.copy
ansible-doc user # show doc for ansible.builtin.user

I have a directory for inventory that is separate from directory, where I have the plays.
That way I can have inventory and plays in two separate Git repos.
Same play works for multiple sites, but each have inventory of their own.

Then I have:

$ cat ~/.ansible.cfg
[defaults]
inventory  = /path_to_inv/hosts

The default Ansible config is in /etc/ansible/ and the default inventory is /etc/ansible/hosts

The overall idea is that you have “control-host” that has ansible-core, plays, and inventory.
You run ansible-playbook on that host. It connects to target hosts with ssh and executes the tasks in them.

Ideally, you run ansible as regular account, connect to remotes as regular account, and ansible
uses ‘sudo’ there (if necessary/instructed). Obviously, if you can ‘ssh root@remote’, then you can
skip the sudo setup. Whether that is a safe practice is its own consideration.

You used awk in your skript to read /etc/passwd. The first task here reads getent passwd and creates variable ansible_facts.getent_passwd to hold all that data.
The second task loops that content and simply prints select fields, but could as well copy a file to the dir …

- hosts: all
  tasks:
  - name: Get user info
    ansible.builtin.getent:
      database: passwd

  - name: Show users (name,uid,homedir)
    ansible.builtin.debug:
      msg: "{{ item.key }} {{ item.value.1 }} {{ item.value.4 }}"
    loop: "{{ ansible_facts.getent_passwd | dict2items }}"
    when:
    - item.value.1|int > 999
    - item.value.1|int < 65534
    loop_control:
      label: "{{ item.key }}"
1 Like

Again, thanks very much. I think you have me convinced. Now I only have to find the time to learn that beast.

I stumbled on Ansible about three years ago. Saw the mesmerizing collection of plays, roles, and variables of a HPC compute cluster. That was too much to take in as a whole, so went to Ansible’s own online docs and started writing tasks and then roles. (Github has plenty of roles, but I rather try to learn by doing.)

1 Like

I know how to set the default session for all users via /etc/accountsservice/user-templates/
and how to specify a particular session for a given user via /var/lib/AccountsService/users/user-name

Does anyone know how to force a particular session of a specific user and not allow them to change it?

I don’t know of any ‘proper’ way of doing this, but I think one (hacky) way of doing this, is something like:

Copy the .desktop file you want to be the ‘fixed’ session to /etc/X11/sessions/ and then run:

setfacl -m u:gdm:--- /usr/share/xsessions

This should prevent the user gdm (the user that runs the greeter) reading the desktop files in /usr/share/xsessions/, so the only choice is the desktop file in /etc/X11/sessions/

I guess the same could be achieved by removing the desktop files in /usr/share/xsessions/ - but they would be replaced by a subsequent update etc

That might work, but it would apply to all users.
For X11 I could always just add an a check in /etc/X11/xinit/Xsession to see if the user was “kiosk” and exec the appropriate session regardless of what they might try to select.

With Wayland I have not found a way to intercept the session startup.

I was hoping for a way to lock a certain user to a certain session.