github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/interfaces/udev/udev.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016-2018 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package udev
    21  
    22  import (
    23  	"fmt"
    24  	"os/exec"
    25  )
    26  
    27  // reloadRules runs three commands that reload udev rule database.
    28  //
    29  // The commands are: udevadm control --reload-rules
    30  //                   udevadm trigger --subsystem-nomatch=input
    31  //                   udevadm settle --timeout=3
    32  // and optionally trigger other subsystems as defined in the interfaces. Eg:
    33  //                   udevadm trigger --subsystem-match=input
    34  //                   udevadm trigger --property-match=ID_INPUT_JOYSTICK=1
    35  func (b *Backend) reloadRules(subsystemTriggers []string) error {
    36  	if b.preseed {
    37  		return nil
    38  	}
    39  
    40  	output, err := exec.Command("udevadm", "control", "--reload-rules").CombinedOutput()
    41  	if err != nil {
    42  		return fmt.Errorf("cannot reload udev rules: %s\nudev output:\n%s", err, string(output))
    43  	}
    44  
    45  	// By default, trigger for all events except the input subsystem since
    46  	// it can cause noticeable blocked input on, for example, classic
    47  	// desktop.
    48  	output, err = exec.Command("udevadm", "trigger", "--subsystem-nomatch=input").CombinedOutput()
    49  	if err != nil {
    50  		return fmt.Errorf("cannot run udev triggers: %s\nudev output:\n%s", err, string(output))
    51  	}
    52  
    53  	// FIXME: track if also should trigger the joystick property if it
    54  	// wasn't already since we are not able to detect interfaces that are
    55  	// removed and set subsystemTriggers correctly. When we can, remove
    56  	// this. Allows joysticks to be removed from the device cgroup on
    57  	// interface disconnect.
    58  	inputJoystickTriggered := false
    59  
    60  	for _, subsystem := range subsystemTriggers {
    61  		if subsystem == "input/joystick" {
    62  			// If one of the interfaces said it uses the input
    63  			// subsystem for joysticks, then trigger the joystick
    64  			// events in a way that is specific to joysticks to not
    65  			// block other inputs.
    66  			output, err = exec.Command("udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1").CombinedOutput()
    67  			if err != nil {
    68  				return fmt.Errorf("cannot run udev triggers for joysticks: %s\nudev output:\n%s", err, string(output))
    69  			}
    70  			inputJoystickTriggered = true
    71  		} else if subsystem == "input/key" {
    72  			// If one of the interfaces said it uses the input
    73  			// subsystem for input keys, then trigger the keys
    74  			// events in a way that is specific to input keys
    75  			// to not block other inputs.
    76  			output, err = exec.Command("udevadm", "trigger", "--property-match=ID_INPUT_KEY=1", "--property-match=ID_INPUT_KEYBOARD!=1").CombinedOutput()
    77  			if err != nil {
    78  				return fmt.Errorf("cannot run udev triggers for keys: %s\nudev output:\n%s", err, string(output))
    79  			}
    80  		} else if subsystem != "" {
    81  			// If one of the interfaces said it uses a subsystem,
    82  			// then do it too.
    83  			output, err = exec.Command("udevadm", "trigger", "--subsystem-match="+subsystem).CombinedOutput()
    84  			if err != nil {
    85  				return fmt.Errorf("cannot run udev triggers for %s subsystem: %s\nudev output:\n%s", subsystem, err, string(output))
    86  			}
    87  
    88  			if subsystem == "input" {
    89  				inputJoystickTriggered = true
    90  			}
    91  		}
    92  	}
    93  
    94  	// FIXME: if not already triggered, trigger the joystick property if it
    95  	// wasn't already since we are not able to detect interfaces that are
    96  	// removed and set subsystemTriggers correctly. When we can, remove
    97  	// this. Allows joysticks to be removed from the device cgroup on
    98  	// interface disconnect.
    99  	if !inputJoystickTriggered {
   100  		output, err = exec.Command("udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1").CombinedOutput()
   101  		if err != nil {
   102  			return fmt.Errorf("cannot run udev triggers for joysticks: %s\nudev output:\n%s", err, string(output))
   103  		}
   104  	}
   105  
   106  	// give our triggered events a chance to be handled before exiting.
   107  	// Ignore errors since we don't want to error on still pending events.
   108  	_ = exec.Command("udevadm", "settle", "--timeout=10").Run()
   109  
   110  	return nil
   111  }