feat: sistema de login automático no portal captivo UniFi UNICEP
This commit is contained in:
commit
f621f2ed80
7 changed files with 565 additions and 0 deletions
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Arquivos de dump de rede e tráfego
|
||||
dump.txt
|
||||
*.dump
|
||||
*.pcap
|
||||
*.log
|
||||
|
||||
# Cache do Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# Arquivos de ambiente e temporários
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
.DS_Store
|
||||
22
99-captive-portal.sh
Executable file
22
99-captive-portal.sh
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env bash
|
||||
# NetworkManager Dispatcher Script para auto-login no portal captivo
|
||||
# Para instalar a nível de sistema (requer root):
|
||||
# sudo cp 99-captive-portal.sh /etc/NetworkManager/dispatcher.d/99-captive-portal.sh
|
||||
# sudo chmod +x /etc/NetworkManager/dispatcher.d/99-captive-portal.sh
|
||||
|
||||
IFACE="$1"
|
||||
ACTION="$2"
|
||||
|
||||
# Ajuste o caminho abaixo se necessário (ou instale em /usr/local/bin/login_portal.py)
|
||||
SCRIPT_PATH="/usr/local/bin/login_portal.py"
|
||||
|
||||
if [ ! -x "$SCRIPT_PATH" ]; then
|
||||
# Tenta localizar o script no diretório do usuário
|
||||
SCRIPT_PATH="$(find /home -maxdepth 4 -name login_portal.py -executable 2>/dev/null | head -n 1)"
|
||||
fi
|
||||
|
||||
if [ "$ACTION" = "up" ] || [ "$ACTION" = "connectivity-change" ]; then
|
||||
if [ -n "$SCRIPT_PATH" ] && [ -x "$SCRIPT_PATH" ]; then
|
||||
"$SCRIPT_PATH" --interface "$IFACE" --quiet > /dev/null 2>&1 &
|
||||
fi
|
||||
fi
|
||||
128
README.md
Normal file
128
README.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
# Login Automático no Portal Captivo UniFi (UNICEP)
|
||||
|
||||
Script e serviço de automação para conexão e liberação automática no portal captivo UniFi da faculdade (UNICEP).
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Como o Login Funciona
|
||||
|
||||
Ao analisar o fluxo de rede do portal captivo em roteadores Ubiquiti UniFi:
|
||||
|
||||
1. **Detecção e Interceptação**:
|
||||
- Ao se conectar ao Wi-Fi (ex: `A5_Alunos_Livre`), a primeira tentativa de navegação (como a verificação de conectividade do sistema operacional em `http://connectivitycheck.gstatic.com/generate_204`) é interceptada pelo AP UniFi.
|
||||
- O AP responde com um redirecionamento HTTP 302 para o daemon de redirecionamento interno:
|
||||
```
|
||||
http://10.0.1.2:8882/redirect?ec=<token_criptografado>
|
||||
```
|
||||
- O daemon na porta 8882 decodifica a sessão e redireciona (HTTP 302) para a interface web na porta 8880:
|
||||
```
|
||||
http://10.0.1.2:8880/guest/s/default/?ap=6c:63:f8:9d:76:12&ec=<token>
|
||||
```
|
||||
*(Onde `ap` é o endereço MAC do ponto de acesso e `ec` é o token criptografado da sessão).*
|
||||
|
||||
2. **Configuração do Portal (`auth: none`)**:
|
||||
- A página consulta `GET /guest/s/default/hotspotconfig`, que retorna:
|
||||
```json
|
||||
{
|
||||
"auth": "none",
|
||||
"payment_enabled": false,
|
||||
"radius_enabled": false,
|
||||
"password_enabled": false,
|
||||
"voucher_enabled": false,
|
||||
"title": "Rede WiFi UNICEP",
|
||||
"button_text": "Login"
|
||||
}
|
||||
```
|
||||
- Trata-se de uma autenticação **"click-through" (auth: none)**: não há usuário, senha nem vouchers.
|
||||
|
||||
3. **A Requisição de Login**:
|
||||
- Quando o botão de login é pressionado, a página dispara uma requisição POST:
|
||||
```http
|
||||
POST http://10.0.1.2:8880/guest/s/default/login HTTP/1.1
|
||||
Referer: http://10.0.1.2:8880/guest/s/default/?ap=<ap_mac>&ec=<token>
|
||||
```
|
||||
- **Corpo da requisição**: Vazio (`null`).
|
||||
- **Cookies**: Desnecessários (`credentials: omit`).
|
||||
- O controlador UniFi recebe a requisição, valida o cliente e o AP via cabeçalho `Referer`, libera o endereço MAC nas tabelas de firewall do AP e responde com redirecionamento HTTP 302. A internet passa a fluir normalmente.
|
||||
|
||||
4. **Tratamento Especial para VPN / WireGuard**:
|
||||
- Se houver uma VPN ativa (como WireGuard com rota padrão), a VPN tenta rotear todo o tráfego antes da liberação do portal, travando conexões locais.
|
||||
- O script resolve isso vinculando todas as requisições de teste e login diretamente à interface Wi-Fi (`--interface wlan0` / `SO_BINDTODEVICE`), contornando a VPN até que a rede local esteja liberada.
|
||||
|
||||
---
|
||||
|
||||
## 📁 Estrutura do Projeto
|
||||
|
||||
* `login_portal.py`: Script principal em Python (suporta modo pontual ou modo `--daemon`).
|
||||
* `install.sh`: Script de instalação rápida do serviço de usuário no `systemd`.
|
||||
* `uninstall.sh`: Script para desativar e remover o serviço.
|
||||
* `unifi-portal.service`: Arquivo de unidade `systemd --user`.
|
||||
* `99-captive-portal.sh`: Script alternativo para NetworkManager Dispatcher (nível de sistema).
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Instalação Rápida (Serviço de Usuário)
|
||||
|
||||
Clone o repositório e execute o instalador:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/muriloscolari/LoginAutomaticoUnicep.git
|
||||
cd LoginAutomaticoUnicep
|
||||
./install.sh
|
||||
```
|
||||
|
||||
O instalador irá:
|
||||
1. Tornar os scripts executáveis.
|
||||
2. Configurar o serviço `systemd --user`.
|
||||
3. Habilitar a inicialização automática junto com seu login no sistema.
|
||||
|
||||
### Gerenciando o Serviço
|
||||
|
||||
```bash
|
||||
# Ver status do serviço
|
||||
systemctl --user status unifi-portal.service
|
||||
|
||||
# Acompanhar logs em tempo real
|
||||
journalctl --user -u unifi-portal.service -f
|
||||
|
||||
# Reiniciar ou parar o serviço
|
||||
systemctl --user restart unifi-portal.service
|
||||
systemctl --user stop unifi-portal.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Uso Manual (Terminal)
|
||||
|
||||
Você também pode executar o script manualmente a qualquer momento:
|
||||
|
||||
```bash
|
||||
# Checa se a internet precisa de liberação e efetua login se necessário
|
||||
./login_portal.py
|
||||
|
||||
# Força o envio do POST mesmo se a internet já parecer ativa
|
||||
./login_portal.py --force
|
||||
|
||||
# Especificar interface manualmente
|
||||
./login_portal.py -i wlan0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💡 Como a Detecção Funciona
|
||||
|
||||
O script possui 3 camadas de segurança:
|
||||
|
||||
1. **Teste de Conectividade (`generate_204`)**:
|
||||
- No Wi-Fi de casa, o Google responde `HTTP 204 No Content`. O script encerra instantaneamente sem fazer nada.
|
||||
- Na faculdade (bloqueado), o AP retorna `HTTP 302` interceptando a conexão.
|
||||
2. **Identificação da Assinatura UniFi**:
|
||||
- Segue os redirecionamentos para confirmar se o destino é um portal UniFi (portas `8880`/`8882` e rota `/guest/s/`).
|
||||
3. **Filtro de SSID**:
|
||||
- Se a internet cair em uma rede residencial (ex: problema na operadora), o script reconhece que o SSID não é da faculdade e que não há portal UniFi, evitando tentativas inválidas.
|
||||
|
||||
---
|
||||
|
||||
## 📄 Licença
|
||||
|
||||
MIT License. Sinta-se livre para usar, modificar e compartilhar.
|
||||
45
install.sh
Executable file
45
install.sh
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
SCRIPT_PATH="$DIR/login_portal.py"
|
||||
SERVICE_DIR="$HOME/.config/systemd/user"
|
||||
SERVICE_FILE="$SERVICE_DIR/unifi-portal.service"
|
||||
|
||||
echo "==> Configurando Login Automático UniFi (UNICEP)..."
|
||||
|
||||
if [ ! -f "$SCRIPT_PATH" ]; then
|
||||
echo "Erro: '$SCRIPT_PATH' não encontrado!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
chmod +x "$SCRIPT_PATH"
|
||||
|
||||
mkdir -p "$SERVICE_DIR"
|
||||
|
||||
cat > "$SERVICE_FILE" <<EOF
|
||||
[Unit]
|
||||
Description=Auto-Login Portal Captivo UniFi (Faculdade)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
ExecStart=$SCRIPT_PATH --daemon
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
EOF
|
||||
|
||||
echo "==> Recarregando systemd user daemon..."
|
||||
systemctl --user daemon-reload
|
||||
|
||||
echo "==> Habilitando e iniciando o serviço unifi-portal.service..."
|
||||
systemctl --user enable --now unifi-portal.service
|
||||
|
||||
echo ""
|
||||
echo "✓ Instalação concluída com sucesso!"
|
||||
echo "Status do serviço:"
|
||||
systemctl --user status unifi-portal.service --no-pager
|
||||
317
login_portal.py
Executable file
317
login_portal.py
Executable file
|
|
@ -0,0 +1,317 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
login_portal.py - Automatização de login no portal captivo UniFi (UNICEP)
|
||||
Funciona tanto em modo único (CLI) quanto em modo daemon / serviço.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Forçar flush de logs imediatamente no systemd / terminal
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(line_buffering=True)
|
||||
import subprocess
|
||||
import time
|
||||
import argparse
|
||||
import urllib.parse
|
||||
import re
|
||||
import select
|
||||
|
||||
CHECK_URLS = [
|
||||
"http://connectivitycheck.gstatic.com/generate_204",
|
||||
"http://detectportal.firefox.com/canonical.html",
|
||||
]
|
||||
|
||||
DEFAULT_FALLBACK_PORTAL = "http://10.0.1.2:8880/guest/s/default/"
|
||||
|
||||
def notify(title, message, icon="network-wireless"):
|
||||
"""Envia notificação gráfica no desktop do usuário."""
|
||||
try:
|
||||
subprocess.run(
|
||||
["notify-send", "-a", "Portal Wi-Fi", "-i", icon, title, message],
|
||||
check=False,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def get_active_wifi_interface():
|
||||
"""Detecta a interface Wi-Fi ativa."""
|
||||
try:
|
||||
out = subprocess.check_output(
|
||||
["nmcli", "-t", "-f", "DEVICE,TYPE,STATE", "device"],
|
||||
text=True,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
for line in out.strip().splitlines():
|
||||
parts = line.split(":")
|
||||
if len(parts) >= 3 and parts[1] == "wifi" and "conectado" in parts[2]:
|
||||
return parts[0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
out = subprocess.check_output(["ip", "route", "show", "default"], text=True, stderr=subprocess.DEVNULL)
|
||||
m = re.search(r"dev\s+(\S+)", out)
|
||||
if m:
|
||||
return m.group(1)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return "wlan0"
|
||||
|
||||
def get_active_ssid():
|
||||
"""Obtém o nome (SSID) da rede Wi-Fi conectada."""
|
||||
try:
|
||||
out = subprocess.check_output(
|
||||
["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show", "--active"],
|
||||
text=True,
|
||||
stderr=subprocess.DEVNULL
|
||||
)
|
||||
for line in out.strip().splitlines():
|
||||
parts = line.split(":")
|
||||
if len(parts) >= 2 and parts[1] in ("802-11-wireless", "wifi"):
|
||||
return parts[0]
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
def is_likely_college_network(ssid):
|
||||
"""
|
||||
Verifica se o SSID atual corresponde à rede da faculdade.
|
||||
Exemplos: A5_Alunos_Livre, UNICEP, Alunos, etc.
|
||||
"""
|
||||
if not ssid:
|
||||
return True # Se não puder identificar o SSID, confia na detecção do portal
|
||||
keywords = ["alunos", "unicep", "livre", "faculdade"]
|
||||
return any(k in ssid.lower() for k in keywords)
|
||||
|
||||
def curl_cmd(args, iface=None, timeout=5):
|
||||
"""
|
||||
Executa o curl vinculando à interface Wi-Fi (--interface <iface>).
|
||||
Isso é crucial caso o WireGuard / VPN esteja ativo, impedindo que a VPN
|
||||
sequestre o tráfego antes de a rede local ter sido liberada.
|
||||
"""
|
||||
cmd = ["curl", "-s"]
|
||||
if iface:
|
||||
cmd.extend(["--interface", iface])
|
||||
cmd.extend(["--connect-timeout", str(timeout), "--max-time", str(timeout * 2)])
|
||||
cmd.extend(args)
|
||||
return subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||
|
||||
def check_internet(iface):
|
||||
"""
|
||||
Verifica se a internet já está acessível através da interface.
|
||||
Retorna (True, '204', url) se o acesso livre foi confirmado.
|
||||
"""
|
||||
for url in CHECK_URLS:
|
||||
res = curl_cmd(["-o", "/dev/null", "-w", "%{http_code}", url], iface=iface, timeout=4)
|
||||
code = res.stdout.strip()
|
||||
if code == "204":
|
||||
return True, code, url
|
||||
return False, code or "000", CHECK_URLS[0]
|
||||
|
||||
def discover_portal(iface):
|
||||
"""
|
||||
Descobre o endereço do portal captivo seguindo os redirecionamentos (302/307).
|
||||
No UniFi, o AP redireciona para a porta 8882 (/redirect?ec=...)
|
||||
e depois para a porta 8880 (/guest/s/default/?ap=...&ec=...).
|
||||
"""
|
||||
for url in CHECK_URLS:
|
||||
res = curl_cmd([
|
||||
"-L",
|
||||
"-o", "/dev/null",
|
||||
"-w", "%{url_effective} | %{http_code}",
|
||||
url
|
||||
], iface=iface, timeout=6)
|
||||
|
||||
output = res.stdout.strip()
|
||||
if " | " in output:
|
||||
final_url, code = output.split(" | ", 1)
|
||||
final_url = final_url.strip()
|
||||
if "8880" in final_url or "8882" in final_url or "/guest/s/" in final_url:
|
||||
return final_url
|
||||
return None
|
||||
|
||||
def perform_login(portal_url, iface, quiet=False):
|
||||
"""
|
||||
Realiza a requisição POST para autorizar o cliente no portal UniFi.
|
||||
"""
|
||||
parsed = urllib.parse.urlparse(portal_url)
|
||||
|
||||
# Se a URL encontrada for o redirect intermediário na 8882, segue para a 8880
|
||||
if ":8882" in portal_url or "/redirect" in parsed.path:
|
||||
res = curl_cmd(["-L", "-o", "/dev/null", "-w", "%{url_effective}", portal_url], iface=iface, timeout=5)
|
||||
portal_url = res.stdout.strip() or portal_url
|
||||
parsed = urllib.parse.urlparse(portal_url)
|
||||
|
||||
# Monta a URL de login mantendo o caminho do site (ex: /guest/s/default/login)
|
||||
path = parsed.path
|
||||
if not path.endswith("/"):
|
||||
path_dir = path[:path.rfind("/") + 1]
|
||||
else:
|
||||
path_dir = path
|
||||
|
||||
login_endpoint = f"{parsed.scheme}://{parsed.netloc}{path_dir}login"
|
||||
|
||||
if not quiet:
|
||||
print(f"[*] Enviando requisição de login para: {login_endpoint}")
|
||||
print(f"[*] Referer: {portal_url}")
|
||||
|
||||
# No UniFi com 'auth: none', o login é um POST com body vazio (null)
|
||||
# e o Referer apontando para a página onde constam os parâmetros 'ap' e 'ec'
|
||||
headers = [
|
||||
"-H", f"Referer: {portal_url}",
|
||||
"-H", "Accept: */*",
|
||||
"-H", "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
]
|
||||
|
||||
res = curl_cmd([
|
||||
"-X", "POST",
|
||||
"-o", "/dev/null",
|
||||
"-w", "%{http_code}",
|
||||
*headers,
|
||||
login_endpoint
|
||||
], iface=iface, timeout=6)
|
||||
|
||||
return res.stdout.strip()
|
||||
|
||||
def attempt_login(iface, force=False, quiet=False):
|
||||
"""
|
||||
Fluxo completo: verifica conectividade, descobre portal, envia POST e valida.
|
||||
Retorna True se conectado com sucesso, False caso contrário.
|
||||
"""
|
||||
ssid = get_active_ssid()
|
||||
|
||||
if not force:
|
||||
has_internet, code, check_url = check_internet(iface)
|
||||
if has_internet:
|
||||
if not quiet:
|
||||
print(f"[✓] Conexão com a internet já está ativa (HTTP {code} em {check_url}). Login desnecessário.")
|
||||
return True
|
||||
else:
|
||||
if not quiet:
|
||||
print(f"[!] Sem internet livre (HTTP {code}). Iniciando login no portal...")
|
||||
else:
|
||||
if not quiet:
|
||||
print("[!] Modo --force: enviando requisição de login...")
|
||||
|
||||
portal_url = discover_portal(iface)
|
||||
if not portal_url:
|
||||
# Se não detectou portal e o SSID não tem relação com a faculdade, ignora
|
||||
if not force and not is_likely_college_network(ssid):
|
||||
if not quiet:
|
||||
print(f"[i] Rede conectada ('{ssid}') não possui portal captivo da faculdade detectado. Nenhuma ação necessária.")
|
||||
return True
|
||||
|
||||
if not quiet:
|
||||
print(f"[!] Não foi possível detectar a URL por redirecionamento. Tentando fallback: {DEFAULT_FALLBACK_PORTAL}")
|
||||
portal_url = DEFAULT_FALLBACK_PORTAL
|
||||
else:
|
||||
if not quiet:
|
||||
print(f"[✓] Portal detectado em: {portal_url}")
|
||||
|
||||
login_code = perform_login(portal_url, iface, quiet=quiet)
|
||||
if not quiet:
|
||||
print(f"[i] Resposta do login: HTTP {login_code}")
|
||||
|
||||
# Aguarda 1.5s para aplicação das regras no AP UniFi
|
||||
time.sleep(1.5)
|
||||
|
||||
has_internet, code, _ = check_internet(iface)
|
||||
if has_internet:
|
||||
if not quiet:
|
||||
print("[✓] Login efetuado com sucesso! Internet liberada.")
|
||||
notify("Wi-Fi Conectado", f"Login efetuado com sucesso na rede {ssid or 'da faculdade'}!")
|
||||
return True
|
||||
else:
|
||||
if not quiet:
|
||||
print(f"[✗] Falha na liberação da internet após login (HTTP {code}).")
|
||||
notify("Falha no Login Wi-Fi", f"Não foi possível liberar o acesso na rede {ssid or 'da faculdade'}.", icon="dialog-error")
|
||||
return False
|
||||
|
||||
def run_daemon(iface, interval=60):
|
||||
"""
|
||||
Executa como serviço em background:
|
||||
1. Monitora eventos do NetworkManager via 'nmcli monitor'
|
||||
2. Executa checagem periódica a cada 'interval' segundos (caso a sessão expire)
|
||||
"""
|
||||
print(f"[+] Iniciando daemon de login automático (intervalo: {interval}s)...")
|
||||
last_check_time = 0
|
||||
COOLDOWN = 10 # Não tenta logar mais de 1x a cada 10s
|
||||
|
||||
# Testa imediatamente ao iniciar
|
||||
active_iface = iface or get_active_wifi_interface()
|
||||
attempt_login(active_iface, quiet=False)
|
||||
last_check_time = time.time()
|
||||
|
||||
# Inicia o monitor do NetworkManager
|
||||
proc = subprocess.Popen(
|
||||
["nmcli", "monitor"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.DEVNULL,
|
||||
text=True
|
||||
)
|
||||
|
||||
try:
|
||||
while True:
|
||||
# Espera por eventos do nmcli ou até o timeout do intervalo periódico
|
||||
r, _, _ = select.select([proc.stdout], [], [], interval)
|
||||
now = time.time()
|
||||
|
||||
if r:
|
||||
line = proc.stdout.readline()
|
||||
if not line:
|
||||
# Se o nmcli monitor fechou por algum motivo, reinicia
|
||||
time.sleep(2)
|
||||
proc = subprocess.Popen(["nmcli", "monitor"], stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True)
|
||||
continue
|
||||
|
||||
line_str = line.strip()
|
||||
# Se for evento de conectividade ou rede
|
||||
if any(w in line_str.lower() for w in ["conectado", "conectividade", "connectivity", "connection", "portal"]):
|
||||
if now - last_check_time >= COOLDOWN:
|
||||
time.sleep(2) # Dá tempo do DHCP estabilizar
|
||||
active_iface = iface or get_active_wifi_interface()
|
||||
print(f"[*] Evento de rede detectado: {line_str}")
|
||||
attempt_login(active_iface, quiet=False)
|
||||
last_check_time = time.time()
|
||||
else:
|
||||
# Timeout periódico: checagem de saúde
|
||||
if now - last_check_time >= interval:
|
||||
active_iface = iface or get_active_wifi_interface()
|
||||
has_internet, _, _ = check_internet(active_iface)
|
||||
if not has_internet:
|
||||
print("[*] Checagem periódica detectou perda de acesso. Efetuando login...")
|
||||
attempt_login(active_iface, quiet=False)
|
||||
last_check_time = time.time()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print("\n[+] Encerrando daemon...")
|
||||
finally:
|
||||
try:
|
||||
proc.terminate()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Auto-login no portal captivo UniFi (UNICEP).")
|
||||
parser.add_argument("-i", "--interface", help="Interface de rede Wi-Fi (ex: wlan0)")
|
||||
parser.add_argument("-f", "--force", action="store_true", help="Força o login mesmo se a internet parecer ativa")
|
||||
parser.add_argument("-q", "--quiet", action="store_true", help="Modo silencioso (sem prints informativos)")
|
||||
parser.add_argument("-d", "--daemon", action="store_true", help="Executa continuamente como serviço em background")
|
||||
parser.add_argument("--interval", type=int, default=60, help="Intervalo de checagem periódica no modo daemon (padrão: 60s)")
|
||||
args = parser.parse_args()
|
||||
|
||||
iface = args.interface or get_active_wifi_interface()
|
||||
|
||||
if args.daemon:
|
||||
run_daemon(iface, interval=args.interval)
|
||||
return 0
|
||||
|
||||
success = attempt_login(iface, force=args.force, quiet=args.quiet)
|
||||
return 0 if success else 1
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
13
unifi-portal.service
Normal file
13
unifi-portal.service
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
[Unit]
|
||||
Description=Auto-Login Portal Captivo UniFi (Faculdade)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=PYTHONUNBUFFERED=1
|
||||
ExecStart=%h/Documentos/loginAutomatico/login_portal.py --daemon
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
24
uninstall.sh
Executable file
24
uninstall.sh
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
SERVICE_FILE="$HOME/.config/systemd/user/unifi-portal.service"
|
||||
|
||||
echo "==> Desinstalando unifi-portal.service..."
|
||||
|
||||
if systemctl --user is-active --quiet unifi-portal.service 2>/dev/null; then
|
||||
echo "==> Parando o serviço..."
|
||||
systemctl --user stop unifi-portal.service
|
||||
fi
|
||||
|
||||
if systemctl --user is-enabled --quiet unifi-portal.service 2>/dev/null; then
|
||||
echo "==> Desabilitando o serviço..."
|
||||
systemctl --user disable unifi-portal.service
|
||||
fi
|
||||
|
||||
if [ -f "$SERVICE_FILE" ]; then
|
||||
rm -f "$SERVICE_FILE"
|
||||
echo "==> Arquivo de serviço removido."
|
||||
fi
|
||||
|
||||
systemctl --user daemon-reload
|
||||
echo "✓ Desinstalação concluída."
|
||||
Loading…
Reference in a new issue