github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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 ReloadRules(subsystemTriggers []string) error {
    36  	output, err := exec.Command("udevadm", "control", "--reload-rules").CombinedOutput()
    37  	if err != nil {
    38  		return fmt.Errorf("cannot reload udev rules: %s\nudev output:\n%s", err, string(output))
    39  	}
    40  
    41  	// By default, trigger for all events except the input subsystem since
    42  	// it can cause noticeable blocked input on, for example, classic
    43  	// desktop.
    44  	output, err = exec.Command("udevadm", "trigger", "--subsystem-nomatch=input").CombinedOutput()
    45  	if err != nil {
    46  		return fmt.Errorf("cannot run udev triggers: %s\nudev output:\n%s", err, string(output))
    47  	}
    48  
    49  	// FIXME: track if also should trigger the joystick property if it
    50  	// wasn't already since we are not able to detect interfaces that are
    51  	// removed and set subsystemTriggers correctly. When we can, remove
    52  	// this. Allows joysticks to be removed from the device cgroup on
    53  	// interface disconnect.
    54  	inputJoystickTriggered := false
    55  
    56  	for _, subsystem := range subsystemTriggers {
    57  		if subsystem == "input/joystick" {
    58  			// If one of the interfaces said it uses the input
    59  			// subsystem for joysticks, then trigger the joystick
    60  			// events in a way that is specific to joysticks to not
    61  			// block other inputs.
    62  			output, err = exec.Command("udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1").CombinedOutput()
    63  			if err != nil {
    64  				return fmt.Errorf("cannot run udev triggers for joysticks: %s\nudev output:\n%s", err, string(output))
    65  			}
    66  			inputJoystickTriggered = true
    67  		} else if subsystem == "input/key" {
    68  			// If one of the interfaces said it uses the input
    69  			// subsystem for input keys, then trigger the keys
    70  			// events in a way that is specific to input keys
    71  			// to not block other inputs.
    72  			output, err = exec.Command("udevadm", "trigger", "--property-match=ID_INPUT_KEY=1", "--property-match=ID_INPUT_KEYBOARD!=1").CombinedOutput()
    73  			if err != nil {
    74  				return fmt.Errorf("cannot run udev triggers for keys: %s\nudev output:\n%s", err, string(output))
    75  			}
    76  		} else if subsystem != "" {
    77  			// If one of the interfaces said it uses a subsystem,
    78  			// then do it too.
    79  			output, err = exec.Command("udevadm", "trigger", "--subsystem-match="+subsystem).CombinedOutput()
    80  			if err != nil {
    81  				return fmt.Errorf("cannot run udev triggers for %s subsystem: %s\nudev output:\n%s", subsystem, err, string(output))
    82  			}
    83  
    84  			if subsystem == "input" {
    85  				inputJoystickTriggered = true
    86  			}
    87  		}
    88  	}
    89  
    90  	// FIXME: if not already triggered, trigger the joystick property if it
    91  	// wasn't already since we are not able to detect interfaces that are
    92  	// removed and set subsystemTriggers correctly. When we can, remove
    93  	// this. Allows joysticks to be removed from the device cgroup on
    94  	// interface disconnect.
    95  	if !inputJoystickTriggered {
    96  		output, err = exec.Command("udevadm", "trigger", "--property-match=ID_INPUT_JOYSTICK=1").CombinedOutput()
    97  		if err != nil {
    98  			return fmt.Errorf("cannot run udev triggers for joysticks: %s\nudev output:\n%s", err, string(output))
    99  		}
   100  	}
   101  
   102  	// give our triggered events a chance to be handled before exiting.
   103  	// Ignore errors since we don't want to error on still pending events.
   104  	_ = exec.Command("udevadm", "settle", "--timeout=10").Run()
   105  
   106  	return nil
   107  }