---
- name: Set ansible_facts required by role and install packages
  include_tasks: set_facts_packages.yml

- name: Determine if system is booted with systemd
  when: __selinux_is_booted is not defined
  block:
    - name: Run systemctl
      # noqa command-instead-of-module
      command: systemctl is-system-running
      register: __is_system_running
      changed_when: false
      failed_when: false

    - name: Require installed systemd
      fail:
        msg: "Error: This role requires systemd to be installed."
      when: '"No such file or directory" in __is_system_running.msg | d("")'

    - name: Set flag to indicate that systemd runtime operations are available
      set_fact:
        # see https://www.man7.org/linux/man-pages/man1/systemctl.1.html#:~:text=is-system-running%20output
        __selinux_is_booted: "{{ __is_system_running.stdout != 'offline' }}"

- name: Set permanent SELinux state
  vars:
    __update_kernel_param: "{{ ansible_facts['os_family'] == 'RedHat' and
      ansible_facts['distribution_major_version'] is version('8', '>=') and
      ansible_version.full is version('2.10', '>=') }}"
  block:
    - name: Set permanent SELinux state if enabled
      # noqa args[module] fqcn[action]
      selinux:
        state: "{{ selinux_state | default(ansible_selinux.config_mode, true) }}"
        policy: "{{ selinux_policy | default(ansible_selinux.type, true) }}"
        update_kernel_param: "{{ true if __update_kernel_param else omit }}"
      register: selinux_mod_output_enabled
      when:
        - ansible_selinux.status == "enabled"
        - (not selinux_state is none and selinux_state | length > 0) or
          (not selinux_policy is none and selinux_policy | length > 0)

    - name: Set permanent SELinux state if disabled
      # noqa args[module] fqcn[action]
      selinux:
        state: "{{ selinux_state }}"
        policy: "{{ selinux_policy | default('targeted', true) }}"
        update_kernel_param: "{{ true if __update_kernel_param else omit }}"
      register: selinux_mod_output_disabled
      when:
        - ansible_selinux.status == "disabled"
        - not selinux_state is none
        - selinux_state | length > 0

    - name: Set selinux_reboot_required
      set_fact:
        selinux_reboot_required: "{{ selinux_mod_output_enabled.reboot_required
          if (selinux_mod_output_enabled.reboot_required is defined) else
          (selinux_mod_output_disabled.reboot_required | default(false)) }}"

    - name: Add or remove selinux=0 from args as needed
      when:
        - not __update_kernel_param
        - selinux_reboot_required | bool
      include_role:
        name: fedora.linux_system_roles.bootloader
      vars:
        bootloader_settings:
          - kernel: ALL
            options:
              - name: selinux
                value: "0"
                state: "{{ 'present'
                  if selinux_mod_output_enabled.reboot_required | default(false)
                  else 'absent' }}"

    - name: Fail if reboot is required
      fail:
        msg: "Reboot is required to apply changes. Re-execute the role after boot."
      when:
        - selinux_reboot_required
        - __selinux_is_booted | bool

- name: Warn if SELinux is disabled
  debug:
    msg: "SELinux is disabled on system - some SELinux modules can crash"
  when: ansible_selinux.status == "disabled"

- name: Drop all local modifications
  command: /usr/sbin/semanage -i -
  args:
    stdin: "{{ drop_local_modifications }}"
  when: selinux_all_purge | bool
  notify: __selinux_reload_policy
  changed_when: true

- name: Purge all SELinux boolean local modifications
  command: /usr/sbin/semanage boolean -D
  when: selinux_booleans_purge | bool
  changed_when: true

- name: Purge all SELinux file context local modifications
  command: /usr/sbin/semanage fcontext -D
  when: selinux_fcontexts_purge | bool
  changed_when: true

- name: Purge all SELinux port local modifications
  command: /usr/sbin/semanage port -D
  when: selinux_ports_purge | bool
  changed_when: true

- name: Purge all SELinux login local modifications
  command: /usr/sbin/semanage login -D
  when: selinux_logins_purge | bool
  changed_when: true

- name: Set SELinux booleans
  # noqa fqcn[action]
  seboolean:
    name: "{{ __selinux_item.name }}"
    state: "{{ __selinux_item.state }}"
    persistent: "{{ __selinux_item.persistent |
                  default(ansible_selinux.status == 'disabled') }}"
    ignore_selinux_state: "{{ ansible_selinux.status == 'disabled' }}"
  with_items: "{{ selinux_booleans }}"
  loop_control:
    loop_var: __selinux_item

- name: Set SELinux file contexts
  community.general.sefcontext:
    target: "{{ __selinux_item.target }}"
    setype: "{{ __selinux_item.setype }}"
    ftype: "{{ __selinux_item.ftype | default('a') }}"
    state: "{{ __selinux_item.state | default('present') }}"
    selevel: "{{ __selinux_item.selevel | default(omit) }}"
    seuser: "{{ __selinux_item.seuser | default(omit) }}"
    ignore_selinux_state: "{{ ansible_selinux.status == 'disabled' }}"
  with_items: "{{ selinux_fcontexts }}"
  loop_control:
    loop_var: __selinux_item

- name: Set an SELinux label on a port
  local_seport:
    ports: "{{ __selinux_item.ports }}"
    proto: "{{ __selinux_item.proto | default('tcp') }}"
    setype: "{{ __selinux_item.setype }}"
    state: "{{ __selinux_item.state | default('present') }}"
    local: "{{ __selinux_item.local | default(False) }}"
    ignore_selinux_state: "{{ ansible_selinux.status == 'disabled' }}"
  with_items: "{{ selinux_ports }}"
  loop_control:
    loop_var: __selinux_item

- name: Set linux user to SELinux user mapping
  community.general.selogin:
    login: "{{ __selinux_item.login }}"
    seuser: "{{ __selinux_item.seuser }}"
    serange: "{{ __selinux_item.serange | default('s0') }}"
    state: "{{ __selinux_item.state | default('present') }}"
    reload: "{{ __selinux_item.reload | default(False) }}"
    ignore_selinux_state: "{{ ansible_selinux.status == 'disabled' }}"
  with_items: "{{ selinux_logins }}"
  notify: __selinux_reload_policy
  loop_control:
    loop_var: __selinux_item

- name: Get SELinux modules facts
  selinux_modules_facts:

- name: Load SELinux modules
  include_tasks: selinux_load_module.yml
  vars:
    mod_name: "{{ __selinux_item.name |
      default(__selinux_item.path | default('') | basename | splitext |
              first) }}"
    state: "{{ __selinux_item.state | default('enabled') }}"
    priority: "{{ __selinux_item.priority | default('400') }}"
    # yamllint disable rule:line-length
    checksum: "{{ selinux_installed_modules[mod_name][priority]['checksum'] | default('sha256:') }}"
  when: selinux_modules is defined
  loop: "{{ selinux_modules | flatten(levels=1) }}"
  loop_control:
    loop_var: __selinux_item

- name: Restore SELinux labels on filesystem tree
  command: /sbin/restorecon -R -F -v {{ restorecon_threads }} {{ __selinux_item }}
  with_items: "{{ selinux_restore_dirs }}"
  loop_control:
    loop_var: __selinux_item
  register: restorecon_cmd
  changed_when: '"Relabeled" in restorecon_cmd.stdout'

- name: Restore SELinux labels on filesystem tree in check mode
  command: /sbin/restorecon -R -F -v -n {{ restorecon_threads }} {{ __selinux_item }}
  with_items: "{{ selinux_restore_dirs }}"
  loop_control:
    loop_var: __selinux_item
  register: restorecon_cmd
  changed_when: '"Would relabel" in restorecon_cmd.stdout'
  check_mode: false
  when: ansible_check_mode
