iotsploit-cli 0.0.6__py3-none-any.whl

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.
@@ -0,0 +1 @@
1
+ """IoTSploit CLI package."""
@@ -0,0 +1 @@
1
+ # Commands package for SAT Shell
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env python
2
+
3
+ class BaseCommands:
4
+ """Base class for all command modules"""
5
+ pass
@@ -0,0 +1,536 @@
1
+ #!/usr/bin/env python
2
+
3
+ import cmd2
4
+ from cmd2 import ansi
5
+ from typing import Dict
6
+ from .base_commands import BaseCommands
7
+ from iotsploit_django.tools.monitor_mgr import SystemMonitor
8
+ from iotsploit_django.tools.input_mgr import Input_Mgr
9
+ from iotsploit_django.adapters.django.device_registry_factory import get_device_registry
10
+ from iotsploit_core.utils import iots_logger
11
+
12
+ logger = iots_logger.get_logger(__name__)
13
+
14
+
15
+ class DeviceCommands(BaseCommands):
16
+ """Device-related commands for the SAT Shell"""
17
+
18
+ @cmd2.with_category('Device Commands')
19
+ def do_device_info(self, arg):
20
+ 'Show IoTSploit Host Info'
21
+ logger.info(ansi.style("Host Device Info:", fg=ansi.Fg.CYAN))
22
+
23
+ pi_monitor = SystemMonitor.create_monitor("raspberry_pi")
24
+ device_info = SystemMonitor.monitor_device(pi_monitor)
25
+
26
+ for key, value in device_info.items():
27
+ if isinstance(value, dict):
28
+ logger.info(ansi.style(f" {key}:", fg=ansi.Fg.CYAN))
29
+ for sub_key, sub_value in value.items():
30
+ logger.info(ansi.style(f" {sub_key}: {sub_value}", fg=ansi.Fg.CYAN))
31
+ else:
32
+ logger.info(ansi.style(f" {key}: {value}", fg=ansi.Fg.CYAN))
33
+
34
+ @cmd2.with_category('Device Commands')
35
+ def do_list_device_drivers(self, arg):
36
+ 'List all available device plugins'
37
+ available_devices = self.device_driver_manager.list_drivers()
38
+ if available_devices:
39
+ logger.info(ansi.style("Available device plugins:", fg=ansi.Fg.CYAN))
40
+ for device in available_devices:
41
+ logger.info(ansi.style(f" - {device}", fg=ansi.Fg.CYAN))
42
+ else:
43
+ logger.info(ansi.style("No device plugins available.", fg=ansi.Fg.YELLOW))
44
+
45
+ do_lsdrv = do_list_device_drivers
46
+
47
+ @cmd2.with_category('Device Commands')
48
+ def do_list_devices(self, arg):
49
+ 'List all devices stored in the database'
50
+ try:
51
+ devices = self.device_manager.get_all_devices()
52
+
53
+ if not devices:
54
+ logger.info(ansi.style("No devices found in the database.", fg=ansi.Fg.YELLOW))
55
+ return
56
+
57
+ logger.info(ansi.style("Devices in the database:", fg=ansi.Fg.CYAN))
58
+ for device in devices:
59
+ logger.info(ansi.style(f" - ID: {device['device_id']}", fg=ansi.Fg.GREEN))
60
+ logger.info(f" Name: {device['name']}")
61
+ logger.info(f" Type: {device['device_type']}")
62
+
63
+ if device['device_type'] == 'Serial':
64
+ logger.info(f" Port: {device['attributes'].get('port', 'N/A')}")
65
+ logger.info(f" Baud Rate: {device['attributes'].get('baud_rate', 'N/A')}")
66
+ elif device['device_type'] == 'USB':
67
+ logger.info(f" Vendor ID: {device['attributes'].get('vendor_id', 'N/A')}")
68
+ logger.info(f" Product ID: {device['attributes'].get('product_id', 'N/A')}")
69
+
70
+ logger.info(f" Attributes: {device['attributes']}")
71
+ logger.info(" ---")
72
+
73
+ except Exception as e:
74
+ logger.error(ansi.style(f"Error listing devices: {str(e)}", fg=ansi.Fg.RED))
75
+
76
+ # Alias for list_devices
77
+ do_lsdev = do_list_devices
78
+
79
+ @cmd2.with_category('Device Commands')
80
+ def do_execute_device_command(self, arg):
81
+ 'Send a command to a device. Usage: device_command [command_string]'
82
+ try:
83
+ # Check if we need to select a device first
84
+ if not hasattr(self, '_current_device') or not hasattr(self, '_current_driver'):
85
+ logger.info(ansi.style("No device selected. Please select a device first.", fg=ansi.Fg.YELLOW))
86
+ if not self._select_device():
87
+ return
88
+
89
+ # Now we can be sure we have a device selected
90
+ if not arg:
91
+ # If no command provided, list available commands and let user select one
92
+ commands = self.device_driver_manager.get_plugin_commands(self._current_plugin)
93
+ if commands:
94
+ logger.info(ansi.style(f"\nAvailable commands for {self._current_plugin}:", fg=ansi.Fg.CYAN))
95
+
96
+ # Create list of command choices with descriptions
97
+ cmd_choices = [f"{cmd:<10} - {desc}" for cmd, desc in commands.items()]
98
+
99
+ # Let user select a command
100
+ selected = Input_Mgr.Instance().single_choice(
101
+ "Select command to execute",
102
+ cmd_choices
103
+ )
104
+
105
+ # Extract just the command name from the selection
106
+ selected_cmd = selected.split()[0].strip()
107
+
108
+ # Execute the selected command
109
+ self._current_driver.command(self._current_device, selected_cmd)
110
+ else:
111
+ logger.warning(ansi.style(f"No commands available for driver: {self._current_plugin}", fg=ansi.Fg.YELLOW))
112
+ else:
113
+ # Send the command to the device
114
+ self._current_driver.command(self._current_device, arg)
115
+
116
+ except Exception as e:
117
+ logger.error(ansi.style(f"Error sending command: {str(e)}", fg=ansi.Fg.RED))
118
+ logger.debug("Detailed error:", exc_info=True)
119
+
120
+ def _select_device(self):
121
+ """Helper method to handle device selection process"""
122
+ try:
123
+ # Get available device plugins with connected devices
124
+ available_plugins = [
125
+ driver_name for driver_name, device
126
+ in self.connected_devices.items()
127
+ ]
128
+
129
+ if not available_plugins:
130
+ logger.error(ansi.style("No initialized devices available", fg=ansi.Fg.RED))
131
+ return False
132
+
133
+ # Let user select a plugin
134
+ selected_plugin = Input_Mgr.Instance().single_choice(
135
+ "Select device plugin",
136
+ available_plugins
137
+ )
138
+
139
+ # Get the already connected device
140
+ device = self.connected_devices.get(selected_plugin)
141
+ if not device:
142
+ logger.error(ansi.style(f"Device not found for {selected_plugin}", fg=ansi.Fg.RED))
143
+ return False
144
+
145
+ # Store the current device and driver information
146
+ self._current_device = device
147
+ self._current_driver = self.device_driver_manager.get_driver_instance(selected_plugin)
148
+ self._current_plugin = selected_plugin
149
+
150
+ logger.info(ansi.style(f"Selected device: {device.name}", fg=ansi.Fg.GREEN))
151
+ return True
152
+
153
+ except Exception as e:
154
+ logger.error(ansi.style(f"Error during device selection: {str(e)}", fg=ansi.Fg.RED))
155
+ logger.debug("Detailed error:", exc_info=True)
156
+ return False
157
+
158
+ @cmd2.with_category('Device Commands')
159
+ def do_select_device(self, arg):
160
+ 'Select a device for subsequent commands'
161
+ if self._select_device():
162
+ logger.info(ansi.style("Device selected successfully. Use 'execute_device_command' to send commands.", fg=ansi.Fg.GREEN))
163
+ else:
164
+ logger.error(ansi.style("Device selection failed.", fg=ansi.Fg.RED))
165
+
166
+ # Add alias for select_device
167
+ do_sd = do_select_device
168
+
169
+ @cmd2.with_category('Device Commands')
170
+ def do_switch_device(self, arg):
171
+ 'Switch to a different device'
172
+ if hasattr(self, '_current_device'):
173
+ # Try to disconnect current device if possible
174
+ try:
175
+ if hasattr(self._current_driver, 'disconnect'):
176
+ self._current_driver.disconnect(self._current_device)
177
+ except Exception as e:
178
+ logger.debug(f"Error disconnecting device: {str(e)}")
179
+
180
+ delattr(self, '_current_device')
181
+ delattr(self, '_current_driver')
182
+ delattr(self, '_current_plugin')
183
+
184
+ # Immediately prompt for new device selection
185
+ if self._select_device():
186
+ logger.info(ansi.style("Successfully switched to new device.", fg=ansi.Fg.GREEN))
187
+ else:
188
+ logger.error(ansi.style("Failed to switch device.", fg=ansi.Fg.RED))
189
+
190
+ @cmd2.with_category('Device Commands')
191
+ def do_list_device_commands(self, arg):
192
+ 'List available commands for a device driver'
193
+ try:
194
+ # Get available device plugins
195
+ available_plugins = self.device_driver_manager.list_drivers()
196
+ if not available_plugins:
197
+ logger.error(ansi.style("No device plugins available", fg=ansi.Fg.RED))
198
+ return
199
+
200
+ # Let user select a plugin
201
+ selected_plugin = Input_Mgr.Instance().single_choice(
202
+ "Select device plugin",
203
+ available_plugins
204
+ )
205
+
206
+ # Get commands for the selected plugin
207
+ commands = self.device_driver_manager.get_plugin_commands(selected_plugin)
208
+
209
+ if not commands:
210
+ logger.warning(ansi.style(f"No commands available for plugin: {selected_plugin}", fg=ansi.Fg.YELLOW))
211
+ return
212
+
213
+ # Display commands and their descriptions
214
+ logger.info(ansi.style(f"\nAvailable commands for {selected_plugin}:", fg=ansi.Fg.CYAN))
215
+ for cmd, description in commands.items():
216
+ logger.info(ansi.style(f" {cmd:<10}", fg=ansi.Fg.GREEN) +
217
+ ansi.style(f"- {description}", fg=ansi.Fg.WHITE))
218
+
219
+ except Exception as e:
220
+ logger.error(ansi.style(f"Error listing commands: {str(e)}", fg=ansi.Fg.RED))
221
+ logger.debug("Detailed error:", exc_info=True)
222
+
223
+ # Add an alias for list_device_commands
224
+ do_lscmd = do_list_device_commands
225
+
226
+ @cmd2.with_category('Device Commands')
227
+ def do_scan_devices(self, arg):
228
+ 'Scan for devices and show detailed information'
229
+ try:
230
+ # 获取设备注册表实例
231
+ device_registry = get_device_registry()
232
+ device_registry.initialize() # 确保已初始化
233
+
234
+ # 执行设备扫描
235
+ logger.info(ansi.style("Scanning for devices...", fg=ansi.Fg.CYAN))
236
+ discovered_devices = device_registry.scan_devices()
237
+
238
+ # 获取所有设备(包括已存储的和新发现的)
239
+ all_devices = device_registry.device_store.devices
240
+ device_sources = device_registry.device_store.device_sources # 直接获取设备来源字典
241
+
242
+ # 分类显示设备
243
+ static_devices = {
244
+ device_id: device
245
+ for device_id, device in all_devices.items()
246
+ if device_sources.get(device_id) == "static"
247
+ }
248
+
249
+ dynamic_devices = {
250
+ device_id: device
251
+ for device_id, device in all_devices.items()
252
+ if device_sources.get(device_id) == "dynamic"
253
+ }
254
+
255
+ if static_devices:
256
+ logger.info(ansi.style("\nConfigured Devices:", fg=ansi.Fg.BLUE))
257
+ self._display_devices(static_devices, device_sources)
258
+
259
+ if dynamic_devices:
260
+ logger.info(ansi.style("\nDynamically Discovered Devices:", fg=ansi.Fg.GREEN))
261
+ self._display_devices(dynamic_devices, device_sources)
262
+
263
+ if not static_devices and not dynamic_devices:
264
+ logger.info(ansi.style("No devices found.", fg=ansi.Fg.YELLOW))
265
+
266
+ except Exception as e:
267
+ logger.error(ansi.style(f"Error scanning devices: {str(e)}", fg=ansi.Fg.RED))
268
+ logger.debug("Detailed error:", exc_info=True)
269
+
270
+ def _display_devices(self, devices: Dict, sources: Dict):
271
+ """Helper method to display device information"""
272
+ for device_id, device in devices.items():
273
+ source = sources.get(device_id, "unknown")
274
+ source_color = ansi.Fg.GREEN if source == "dynamic" else ansi.Fg.BLUE
275
+
276
+ # Only display ID, Name, and Source
277
+ logger.info(ansi.style(f"\n Device ID: {device_id}", fg=ansi.Fg.CYAN))
278
+ logger.info(ansi.style(f" Source: {source}", fg=source_color))
279
+ logger.info(f" Name: {device.name}")
280
+ logger.info(" " + "-" * 40) # Separator line
281
+
282
+ # 添加命令别名
283
+ do_scan = do_scan_devices
284
+
285
+ # Add alias for execute_device_command
286
+ do_dc = do_execute_device_command
287
+
288
+ @cmd2.with_category('Device Commands')
289
+ def do_initialize_devices(self, arg):
290
+ 'Auto-initialize all available devices'
291
+ logger.info("Starting device initialization...")
292
+ self._auto_initialize_devices()
293
+
294
+ # Add alias for initialize_devices
295
+ do_initdev = do_initialize_devices
296
+
297
+ @cmd2.with_category('Device Commands')
298
+ def do_get_driver_states(self, arg):
299
+ 'Show enabled/disabled state of all device drivers'
300
+ try:
301
+ driver_states = self.device_driver_manager.get_driver_states()
302
+
303
+ if not driver_states:
304
+ logger.info(ansi.style("No device drivers found.", fg=ansi.Fg.YELLOW))
305
+ return
306
+
307
+ logger.info(ansi.style("\nDevice Driver States:", fg=ansi.Fg.CYAN))
308
+
309
+ # Calculate column widths for alignment
310
+ max_name_len = max(len(name) for name in driver_states.keys())
311
+
312
+ # Display stats
313
+ enabled_count = sum(1 for state in driver_states.values() if state.get("enabled", True))
314
+ disabled_count = len(driver_states) - enabled_count
315
+
316
+ logger.info(ansi.style(f"Total: {len(driver_states)} | Enabled: {enabled_count} | Disabled: {disabled_count}\n", fg=ansi.Fg.BLUE))
317
+
318
+ # Display detailed info for each driver
319
+ for driver_name, state in driver_states.items():
320
+ enabled = state.get("enabled", True)
321
+ status_color = ansi.Fg.GREEN if enabled else ansi.Fg.RED
322
+ status_text = "Enabled" if enabled else "Disabled"
323
+
324
+ logger.info(ansi.style(f"{driver_name:<{max_name_len}} : ", fg=ansi.Fg.CYAN) +
325
+ ansi.style(f"{status_text}", fg=status_color))
326
+
327
+ # Show description if available
328
+ description = state.get("description")
329
+ if description:
330
+ logger.info(f" Description: {description}")
331
+
332
+ # Show last update time if available
333
+ last_updated = state.get("last_updated")
334
+ if last_updated:
335
+ logger.info(f" Last updated: {last_updated}")
336
+
337
+ logger.info("") # Empty line between drivers
338
+
339
+ except Exception as e:
340
+ logger.error(ansi.style(f"Error getting driver states: {str(e)}", fg=ansi.Fg.RED))
341
+ logger.debug("Detailed error:", exc_info=True)
342
+
343
+ # Add alias for get_driver_states
344
+ do_gds = do_get_driver_states
345
+
346
+ @cmd2.with_category('Device Commands')
347
+ def do_enable_driver(self, arg):
348
+ 'Enable a device driver. Usage: enable_driver [driver_name]'
349
+ try:
350
+ # Get list of all drivers
351
+ all_drivers = self.device_driver_manager.list_drivers()
352
+
353
+ if not all_drivers:
354
+ logger.warning(ansi.style("No device drivers available.", fg=ansi.Fg.YELLOW))
355
+ return
356
+
357
+ # If driver name is provided as an argument, use it
358
+ # Otherwise, let user select from available drivers
359
+ driver_name = arg.strip() if arg else None
360
+
361
+ if not driver_name:
362
+ # Get current states to show which are disabled
363
+ states = self.device_driver_manager.get_driver_states()
364
+
365
+ # Create list with status indicators
366
+ driver_choices = []
367
+ for drv in all_drivers:
368
+ status = states.get(drv, {}).get("enabled", True)
369
+ status_text = "Enabled" if status else "Disabled"
370
+ driver_choices.append(f"{drv} [{status_text}]")
371
+
372
+ # Let user select a driver
373
+ selected = Input_Mgr.Instance().single_choice(
374
+ "Select driver to enable",
375
+ driver_choices
376
+ )
377
+
378
+ # Extract driver name from selection
379
+ driver_name = selected.split()[0]
380
+
381
+ # Ask for optional description
382
+ description = Input_Mgr.Instance().string_input(
383
+ "Enter reason for enabling (optional)"
384
+ )
385
+
386
+ # Enable the driver - only pass description if it's not empty
387
+ if description.strip():
388
+ result = self.device_driver_manager.enable_driver(driver_name, description)
389
+ else:
390
+ result = self.device_driver_manager.enable_driver(driver_name)
391
+
392
+ if result.get("status") == "success":
393
+ logger.info(ansi.style(f"Successfully enabled driver: {driver_name}", fg=ansi.Fg.GREEN))
394
+ else:
395
+ logger.error(ansi.style(f"Failed to enable driver: {result.get('message', 'Unknown error')}", fg=ansi.Fg.RED))
396
+
397
+ except Exception as e:
398
+ logger.error(ansi.style(f"Error enabling driver: {str(e)}", fg=ansi.Fg.RED))
399
+ logger.debug("Detailed error:", exc_info=True)
400
+
401
+ # Add alias for enable_driver
402
+ do_ed = do_enable_driver
403
+
404
+ @cmd2.with_category('Device Commands')
405
+ def do_disable_driver(self, arg):
406
+ 'Disable a device driver. Usage: disable_driver [driver_name]'
407
+ try:
408
+ # Get list of all drivers
409
+ all_drivers = self.device_driver_manager.list_drivers()
410
+
411
+ if not all_drivers:
412
+ logger.warning(ansi.style("No device drivers available.", fg=ansi.Fg.YELLOW))
413
+ return
414
+
415
+ # If driver name is provided as an argument, use it
416
+ # Otherwise, let user select from available drivers
417
+ driver_name = arg.strip() if arg else None
418
+
419
+ if not driver_name:
420
+ # Get current states to show which are enabled
421
+ states = self.device_driver_manager.get_driver_states()
422
+
423
+ # Create list with status indicators
424
+ driver_choices = []
425
+ for drv in all_drivers:
426
+ status = states.get(drv, {}).get("enabled", True)
427
+ status_text = "Enabled" if status else "Disabled"
428
+ driver_choices.append(f"{drv} [{status_text}]")
429
+
430
+ # Let user select a driver
431
+ selected = Input_Mgr.Instance().single_choice(
432
+ "Select driver to disable",
433
+ driver_choices
434
+ )
435
+
436
+ # Extract driver name from selection
437
+ driver_name = selected.split()[0]
438
+
439
+ # Warning and confirmation before disabling
440
+ logger.warning(ansi.style("Warning: Disabling a driver will close all connected devices for that driver!", fg=ansi.Fg.YELLOW))
441
+
442
+ confirm = Input_Mgr.Instance().yes_no_input(
443
+ f"Are you sure you want to disable driver '{driver_name}'?",
444
+ default=False
445
+ )
446
+
447
+ if not confirm:
448
+ logger.info("Operation cancelled.")
449
+ return
450
+
451
+ # Ask for optional description
452
+ description = Input_Mgr.Instance().string_input("Enter reason for disabling (optional)")
453
+
454
+ # Disable the driver
455
+ result = self.device_driver_manager.disable_driver(driver_name, description or None)
456
+
457
+ if result.get("status") == "success":
458
+ logger.info(ansi.style(f"Successfully disabled driver: {driver_name}", fg=ansi.Fg.GREEN))
459
+
460
+ # Report on closed devices
461
+ closed_devices = result.get("closed_devices", [])
462
+ if closed_devices:
463
+ logger.info(f"Closed {len(closed_devices)} device(s): {', '.join(closed_devices)}")
464
+
465
+ # Remove from connected_devices if needed
466
+ if driver_name in self.connected_devices:
467
+ del self.connected_devices[driver_name]
468
+ else:
469
+ logger.error(ansi.style(f"Failed to disable driver: {result.get('message', 'Unknown error')}", fg=ansi.Fg.RED))
470
+
471
+ except Exception as e:
472
+ logger.error(ansi.style(f"Error disabling driver: {str(e)}", fg=ansi.Fg.RED))
473
+ logger.debug("Detailed error:", exc_info=True)
474
+
475
+ # Add alias for disable_driver
476
+ do_dd = do_disable_driver
477
+
478
+ @cmd2.with_category('Device Commands')
479
+ def do_device_import(self, arg):
480
+ '''Import devices from a JSON file into the database.
481
+
482
+ Usage: device_import <json_file_path>
483
+
484
+ Example: device_import conf/devices.json
485
+
486
+ JSON file format:
487
+ {
488
+ "devices": [
489
+ {
490
+ "device_id": "serial_001",
491
+ "name": "Serial Device",
492
+ "device_type": "Serial",
493
+ "port": "/dev/ttyUSB0",
494
+ "baud_rate": 115200
495
+ },
496
+ {
497
+ "device_id": "usb_001",
498
+ "name": "USB Device",
499
+ "device_type": "USB",
500
+ "vendor_id": "1234",
501
+ "product_id": "5678"
502
+ }
503
+ ]
504
+ }
505
+ '''
506
+ import os
507
+ import warnings
508
+
509
+ if not arg:
510
+ logger.error(ansi.style("Usage: device_import <json_file_path>", fg=ansi.Fg.RED))
511
+ logger.info(ansi.style("Example: device_import conf/devices.json", fg=ansi.Fg.CYAN))
512
+ return
513
+
514
+ json_file_path = arg.strip()
515
+
516
+ if not os.path.exists(json_file_path):
517
+ logger.error(ansi.style(f"File not found: {json_file_path}", fg=ansi.Fg.RED))
518
+ return
519
+
520
+ try:
521
+ logger.info(ansi.style(f"Importing devices from: {json_file_path}", fg=ansi.Fg.CYAN))
522
+
523
+ # Suppress deprecation warning since we're intentionally using this for import
524
+ with warnings.catch_warnings():
525
+ warnings.simplefilter("ignore", DeprecationWarning)
526
+ self.device_manager.parse_and_set_device_from_json(json_file_path)
527
+
528
+ logger.info(ansi.style("Device import completed successfully!", fg=ansi.Fg.GREEN))
529
+ logger.info(ansi.style("Use 'list_devices' or 'lsdev' to view imported devices.", fg=ansi.Fg.CYAN))
530
+
531
+ except Exception as e:
532
+ logger.error(ansi.style(f"Error importing devices: {str(e)}", fg=ansi.Fg.RED))
533
+ logger.debug("Detailed error:", exc_info=True)
534
+
535
+ # Add alias for device_import
536
+ do_dimport = do_device_import