gitee.com/mysnapcore/mysnapd@v0.1.0/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 // udevadmTrigger runs "udevadm trigger" but ignores an non-zero exit codes. 28 // udevadm only started reporting errors in systemd 248 and in order to 29 // work correctly in LXD these errors need to be ignored. See 30 // https://github.com/systemd/systemd/pull/18684 for some more background 31 // (and https://github.com/lxc/lxd/issues/9526) 32 func udevadmTrigger(args ...string) error { 33 args = append([]string{"trigger"}, args...) 34 output, err := exec.Command("udevadm", args...).CombinedOutput() 35 // can happen when events for some of the devices or all of 36 // them could not be triggered, but we cannot distinguish which of 37 // those happened, in any case snapd invoked udevadm and tried its 38 // best 39 if exitErr, ok := err.(*exec.ExitError); ok { 40 // ignore "normal" exit codes but report e.g. segfaults 41 // that are reported as -1 42 if exitErr.ExitCode() > 0 { 43 return nil 44 } 45 } 46 if err != nil { 47 return fmt.Errorf("%s\nudev output:\n%s", err, string(output)) 48 } 49 return nil 50 } 51 52 // reloadRules runs three commands that reload udev rule database. 53 // 54 // The commands are: udevadm control --reload-rules 55 // udevadm trigger --subsystem-nomatch=input 56 // udevadm settle --timeout=3 57 // and optionally trigger other subsystems as defined in the interfaces. Eg: 58 // udevadm trigger --subsystem-match=input 59 // udevadm trigger --property-match=ID_INPUT_JOYSTICK=1 60 func (b *Backend) reloadRules(subsystemTriggers []string) error { 61 if b.preseed { 62 return nil 63 } 64 65 output, err := exec.Command("udevadm", "control", "--reload-rules").CombinedOutput() 66 if err != nil { 67 return fmt.Errorf("cannot reload udev rules: %s\nudev output:\n%s", err, string(output)) 68 } 69 70 // By default, trigger for all events except the input subsystem since 71 // it can cause noticeable blocked input on, for example, classic 72 // desktop. 73 if err = udevadmTrigger("--subsystem-nomatch=input"); err != nil { 74 return fmt.Errorf("cannot run udev triggers: %s", err) 75 } 76 77 mustTriggerForInputSubsystem := false 78 mustTriggerForInputKeys := false 79 80 for _, subsystem := range subsystemTriggers { 81 if subsystem == "input/key" { 82 mustTriggerForInputKeys = true 83 } else if subsystem == "input" { 84 mustTriggerForInputSubsystem = true 85 } 86 // no `else` branch: we already triggered udevadm for all other 87 // subsystems before by running it with the `--subsystem-nomatch=input` 88 // option, so there's no need to do anything here. 89 } 90 91 if mustTriggerForInputSubsystem { 92 // Trigger for the whole input subsystem 93 if err := udevadmTrigger("--subsystem-match=input"); err != nil { 94 return fmt.Errorf("cannot run udev triggers for input subsystem: %s", err) 95 } 96 } else { 97 // More specific triggers, to avoid blocking keyboards and mice 98 99 if mustTriggerForInputKeys { 100 // If one of the interfaces said it uses the input 101 // subsystem for input keys, then trigger the keys 102 // events in a way that is specific to input keys 103 // to not block other inputs. 104 if err = udevadmTrigger("--property-match=ID_INPUT_KEY=1", "--property-match=ID_INPUT_KEYBOARD!=1"); err != nil { 105 return fmt.Errorf("cannot run udev triggers for keys: %s", err) 106 } 107 } 108 // FIXME: if not already triggered, trigger the joystick property if it 109 // wasn't already since we are not able to detect interfaces that are 110 // removed and set subsystemTriggers correctly. When we can, remove 111 // this. Allows joysticks to be removed from the device cgroup on 112 // interface disconnect. 113 if err := udevadmTrigger("--property-match=ID_INPUT_JOYSTICK=1"); err != nil { 114 return fmt.Errorf("cannot run udev triggers for joysticks: %s", err) 115 } 116 } 117 118 // give our triggered events a chance to be handled before exiting. 119 // Ignore errors since we don't want to error on still pending events. 120 _ = exec.Command("udevadm", "settle", "--timeout=10").Run() 121 122 return nil 123 }