device_input 0.0.1.1 → 0.0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6bc11d4d1313889df6c9b9ff7289bfe1dcd8fff6
4
- data.tar.gz: 1e13f0c7104ad27775e606e4a7a50184a53bef97
3
+ metadata.gz: 42e1a1dde8d9b9884e97bef833d8890d9aaa3a23
4
+ data.tar.gz: 5076d4580ee666d8c82a0f9b924ed94c2890b041
5
5
  SHA512:
6
- metadata.gz: 3c4ad7cf1172d6483e53604b4f94dfc1d490a1364994264acfc8691c72c044170f64d8a8fcb2a8d7518d3f41c91f97c87d4b9be24a344f67a4b3867953969f41
7
- data.tar.gz: e2e275a1bcb94b0cdaeb620fc3d290d88d0f2aca5f4a082ad04d973c0a29c8d98bddd86285e5ff240261dcea60617fbb01387b577aac78acc0edc619756f4e70
6
+ metadata.gz: 3ae5856b3d005ea2b268e931d5c1ac3c73cc8fd1a31ce0e4264b16adc4f70841b3858e8fd5aa771df47631da111eba823ef0ca1f3089c6a6f602a866eb759f58
7
+ data.tar.gz: 5f4c0defc1877124d209cc424f312cd099493f84a1feab65f651210b08475f2d56f4fdfd827b172a8b6ce3527f0e4bc5a7e1ba1301f0dcf8158ec59da1d302a6
data/README.md CHANGED
@@ -1,13 +1,117 @@
1
- # Background
1
+ # Device Input
2
2
 
3
- We want to read events from e.g. `/dev/input/event0` in Ruby.
3
+ *for the Linux kernel*
4
+
5
+ We want to read events from e.g. `/dev/input/event0` in Ruby. For example,
6
+ if you want to see what's happening "on the wire" when you press a special
7
+ function key on a laptop. While this code can be used for the purpose of
8
+ malicious keystroke logging, it is not well suited for it and does not provide
9
+ the root privileges in order to read `/dev/input`. Once you've got the
10
+ privilege to read `/dev/input` it's *game over* anyway.
11
+
12
+ ## Rationale
13
+
14
+ `/dev/input/eventX` is just a character device. Can't we read it with simple
15
+ Unix tooling? Yes and no. First of all, a character device just means that
16
+ it passes bytes (not necessarily characters or strings) from userspace into
17
+ the kernel. Secondarily, the messages (defined as C structs) are in fact
18
+ binary and not strings or conventional characters.
19
+
20
+ Since these are C structs (analagous to a binary message), we need to be able
21
+ to delimit individual messages and decode them. We can't simply read a byte
22
+ at a time and try to make sense of it. In fact, on my system,
23
+ `/dev/input/event0` refuses any read that is not a multiple of the struct /
24
+ message size, so we need to know the message size before even attempting a
25
+ read(), without even a decode().
26
+
27
+ To determine the message size, we need to know the data structure. For a
28
+ long time, it was pretty simple: events are 16 bytes:
29
+
30
+ * timestamp - 8 bytes
31
+ * type - 1 byte
32
+ * code - 1 byte
33
+ * value - 2 bytes
34
+
35
+ However, this is only true for 32-bit platforms. On 64-bit platforms, event
36
+ timestamps became 16 bytes, increasing events from 16 to 24 bytes. This is
37
+ because a timestamp is defined as two `long`s, and `long`s are bigger on
38
+ 64-bit platforms. It's easy to remember:
39
+
40
+ * 32-bit platform: 32-bit `long` (4 bytes)
41
+ * 64-bit platform: 64-bit `long` (8 bytes)
42
+
43
+ `read(/dev/input/event0, 16)` will fail on a 64-bit machine.
44
+
45
+ Your tooling must be aware of this distinction and choose the correct
46
+ underlying data types just to be able to delimit messages and perform a
47
+ successful read. This software does that, decodes the message, maps the
48
+ encoded values to friendly strings for display, and provides both library and
49
+ executable code to assist in examining kernel input events.
50
+
51
+ # Installation
52
+
53
+ Install the gem:
54
+
55
+ ```
56
+ $ gem install device_input # sudo as necessary
57
+ ```
58
+
59
+ Or, if using [Bundler](http://bundler.io/), add to your `Gemfile`:
60
+
61
+ ```
62
+ gem 'device_input', '~> 0.0'
63
+ ```
64
+
65
+ # Usage
66
+
67
+ ## Executable
68
+
69
+ ```
70
+ $ sudo devsniff /dev/input/event0
71
+ ```
72
+
73
+ When the `f` key is pressed:
74
+
75
+ ```
76
+ Misc:ScanCode:33
77
+ Key:F:1
78
+ Sync:Sync:0
79
+ ```
80
+
81
+ And released:
82
+ ```
83
+ Misc:ScanCode:33
84
+ Key:F:0
85
+ Sync:Sync:0
86
+ ```
87
+
88
+ ## Library
89
+
90
+ ```
91
+ require 'device_input'
92
+
93
+ DeviceInput.read_from('/dev/input/event0') do |event|
94
+ puts event
95
+ end
96
+ ```
97
+
98
+ An event has:
99
+
100
+ * `#data` - a Struct of ints (class name Data)
101
+ * `#time` - a Time, accurate to usecs
102
+ * `#type` - a String, possibly UNK-X where X is the integer from `#data`
103
+ * `#code` - a String, possibly UNK-X-Y where X and Y are from `#data`
104
+ * `#value` - a Fixnum (signed)
105
+
106
+ # Research
4
107
 
5
108
  ## Kernel docs
6
109
 
7
110
  * https://www.kernel.org/doc/Documentation/input/input.txt
8
111
  * https://www.kernel.org/doc/Documentation/input/event-codes.txt
9
112
 
10
- These events are defined as C structs with a fixed size in bytes.
113
+ These events are defined as C structs with a fixed size in bytes. See more
114
+ about these structs towards the end of this document.
11
115
 
12
116
  ## Kernel structs
13
117
 
@@ -34,8 +138,7 @@ struct input_event {
34
138
  };
35
139
  ```
36
140
 
37
- What's a `timeval`?
38
- https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/time.h#n15
141
+ What's a [`timeval`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/linux/time.h#n15)?
39
142
 
40
143
  ```
41
144
  struct timeval {
@@ -44,8 +147,7 @@ struct timeval {
44
147
  };
45
148
  ```
46
149
 
47
- What's a `__kernel_time_t`?
48
- https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/posix_types.h#n88
150
+ What's a [`__kernel_time_t`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/posix_types.h#n88)?
49
151
 
50
152
  ```
51
153
  typedef long __kernel_long_t;
@@ -55,10 +157,9 @@ typedef __kernel_long_t __kernel_suseconds_t;
55
157
  typedef __kernel_long_t __kernel_time_t;
56
158
  ```
57
159
 
58
- What's a `__u16`? We're pretty sure it's an unsigned 16 bit integer.
59
- Likewise `__s32` should be a signed 32-bit integer:
60
-
61
- https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/int-l64.h#n23
160
+ What's a [`__u16`](https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/int-l64.h#n23)?
161
+ We're pretty sure it's an unsigned 16 bit integer. Likewise `__s32` should
162
+ be a signed 32-bit integer:
62
163
 
63
164
  ```
64
165
  typedef unsigned short __u16;
@@ -69,7 +170,7 @@ typedef __signed__ int __s32;
69
170
  Why is the value signed? It's meant to be able to communicate an "analog"
70
171
  range, say -127 to +127 as determined by the position of a joystick.
71
172
 
72
- Let's review:
173
+ ## Review
73
174
 
74
175
  `input_event`
75
176
 
@@ -84,18 +185,26 @@ Flattened: `SEC` `USEC` `TYPE` `CODE` `VALUE`
84
185
 
85
186
  How many bytes is a `long`? Well, it's platform-dependent. On a 32-bit
86
187
  platform, you get 32 bits (4 bytes). On a 64-bit platform you get 64 bits
87
- (8 bytes).
188
+ (8 bytes). This means that the event is 16 bytes on a 32-bit machine and
189
+ 24 bytes on a 64-bit machine. Software will need to accommodate.
88
190
 
89
- This means that the event is 16 bytes on a 32-bit machine and 24 bytes on a
90
- 64-bit machine. Software will need to accommodate.
191
+ ## Ruby tools
91
192
 
92
- We can use `RbConfig` and `Array#pack` to help us read these binary structs:
193
+ We can use `RbConfig` and `Array#pack`/`String#unpack` to help us read these
194
+ binary structs:
93
195
 
94
196
  ```
95
197
  FIELD C RbConfig Pack
96
- tv_usec long long l!
198
+ --- --- --- ---
199
+ tv_sec long long l!
97
200
  tv_usec long long l!
98
201
  type __u16 uint16_t S
99
202
  code __u16 uint16_t S
100
203
  value __s32 int32_t l
101
204
  ```
205
+
206
+ # Acknowledgments
207
+
208
+ * Inspired by https://github.com/prullmann/libdevinput (don't use it)
209
+ - also the source of the [event code labels](lib/device_input/codes.rb)
210
+ * Thanks to al2o3-cr from #ruby on Freenode for feedback
@@ -1,13 +1,13 @@
1
1
  module DeviceInput
2
- EVENTS = {}
2
+ CODES = {}
3
3
 
4
4
  # type = Sync
5
- EVENTS[0] = {
5
+ CODES[0] = {
6
6
  0 => 'Sync',
7
7
  }
8
8
 
9
9
  # type = Key
10
- EVENTS[1] = {
10
+ CODES[1] = {
11
11
  0 => 'Reserved',
12
12
  1 => 'Esc',
13
13
  2 => '1',
@@ -358,7 +358,7 @@ module DeviceInput
358
358
  }
359
359
 
360
360
  # type = Relative
361
- EVENTS[2] = {
361
+ CODES[2] = {
362
362
  0 => 'X',
363
363
  1 => 'Y',
364
364
  2 => 'Z',
@@ -369,7 +369,7 @@ module DeviceInput
369
369
  }
370
370
 
371
371
  # type = Absolute
372
- EVENTS[3] = {
372
+ CODES[3] = {
373
373
  0 => 'X',
374
374
  1 => 'Y',
375
375
  2 => 'Z',
@@ -399,7 +399,7 @@ module DeviceInput
399
399
  }
400
400
 
401
401
  # type = Misc
402
- EVENTS[4] = {
402
+ CODES[4] = {
403
403
  0 => 'Serial',
404
404
  1 => 'Pulseled',
405
405
  2 => 'Gesture',
@@ -408,7 +408,7 @@ module DeviceInput
408
408
  }
409
409
 
410
410
  # type = LED
411
- EVENTS[17] = {
411
+ CODES[17] = {
412
412
  0 => 'NumLock',
413
413
  1 => 'CapsLock',
414
414
  2 => 'ScrollLock',
@@ -421,14 +421,14 @@ module DeviceInput
421
421
  }
422
422
 
423
423
  # type = Sound
424
- EVENTS[18] = {
424
+ CODES[18] = {
425
425
  0 => 'Click',
426
426
  1 => 'Bell',
427
427
  2 => 'Tone',
428
428
  }
429
429
 
430
430
  # type = Repeat
431
- EVENTS[20] = {
431
+ CODES[20] = {
432
432
  0 => 'Delay',
433
433
  1 => 'Period',
434
434
  }
data/lib/device_input.rb CHANGED
@@ -14,8 +14,24 @@ module DeviceInput
14
14
  }
15
15
  PACK = DEFINITION.values.map { |v| PACK_MAP.fetch(v) }.join
16
16
 
17
+ # this defines a class, i.e. class Data ...
17
18
  Data = Struct.new(*DEFINITION.keys)
18
19
 
20
+ # these are just labels, not used internally
21
+ TYPES = {
22
+ 0 => 'Sync',
23
+ 1 => 'Key',
24
+ 2 => 'Relative',
25
+ 3 => 'Absolute',
26
+ 4 => 'Misc',
27
+ 17 => 'LED',
28
+ 18 => 'Sound',
29
+ 20 => 'Repeat',
30
+ 21 => 'ForceFeedback',
31
+ 22 => 'Power',
32
+ 23 => 'ForceFeedbackStatus',
33
+ }
34
+
19
35
  # convert Event::Data to a string
20
36
  def self.encode(data)
21
37
  data.values.pack(PACK)
@@ -31,51 +47,39 @@ module DeviceInput
31
47
  end
32
48
 
33
49
  def self.code_str(type_code, code_code)
34
- require 'device_input/events'
35
- DeviceInput::EVENTS.dig(type_code, code_code) ||
50
+ require 'device_input/codes'
51
+ DeviceInput::CODES.dig(type_code, code_code) ||
36
52
  "UNK-#{type_code}-#{code_code}"
37
53
  end
38
54
 
39
55
  NULL_DATA = Data.new(0, 0, 0, 0, 0)
40
56
  NULL_MSG = self.encode(NULL_DATA)
57
+ BYTE_LENGTH = NULL_MSG.length
41
58
 
42
- attr_reader :data, :time, :type, :code, :length
59
+ attr_reader :data, :time, :type, :code
43
60
 
44
61
  def initialize(data)
45
62
  @data = data
46
63
  @time = Time.at(data.tv_sec, data.tv_usec)
47
64
  @type = self.class.type_str(data.type)
48
65
  @code = self.class.code_str(data.type, data.code)
49
- @value = data.value
50
66
  end
51
67
 
52
- def to_s
53
- [@type, @code, @value].join(':')
68
+ def value
69
+ @data.value
54
70
  end
55
71
 
56
- TYPES = {
57
- 0 => 'Sync',
58
- 1 => 'Key',
59
- 2 => 'Relative',
60
- 3 => 'Absolute',
61
- 4 => 'Misc',
62
- 17 => 'LED',
63
- 18 => 'Sound',
64
- 20 => 'Repeat',
65
- 21 => 'ForceFeedback',
66
- 22 => 'Power',
67
- 23 => 'ForceFeedbackStatus',
68
- }
69
-
70
- BYTE_LENGTH = NULL_MSG.length
72
+ def to_s
73
+ [@type, @code, @data.value].join(':')
74
+ end
71
75
  end
72
76
 
73
77
  def self.read_from(filename)
74
78
  File.open(filename, 'r') { |f|
75
79
  loop {
76
80
  bytes = f.read(Event::BYTE_LENGTH)
77
- msg = Event.decode(bytes)
78
- yield Event.new(msg)
81
+ data = Event.decode(bytes)
82
+ yield Event.new(data)
79
83
  }
80
84
  }
81
85
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: device_input
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.1
4
+ version: 0.0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
@@ -36,7 +36,7 @@ files:
36
36
  - README.md
37
37
  - bin/devsniff
38
38
  - lib/device_input.rb
39
- - lib/device_input/events.rb
39
+ - lib/device_input/codes.rb
40
40
  homepage: https://github.com/rickhull/device_input
41
41
  licenses:
42
42
  - GPL-3.0