device_input 0.0.1.1 → 0.0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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