helio.loureiro.eng.br
  • Home
  • Unix
  • Linux
  • Blog
  • Python
  • Programação
  • Tudo
  • Suécia
  1. You are here:  
  2. Home
  3. Unix
  4. Linux

Os artigos mais lidos de 2025

  • Configurando traefik com ssh
  • Configurando o grafana alloy pra monitorar VMs
  • Acessos de robôs nos logs web
  • Configurando o teclado Keychron C3 no Linux
  • Parâmetros de compilação pra Go!

Placa ASUS Pro WS WRX80E-SAGE SE WIFI e nvme não detectado

Details
Written by: Helio Loureiro
Category: Linux
Published: December 05, 2025
Hits: 635
  • linux
  • Ubuntu
  • nvme

Recebi a missão de montar uma máquina que será usada pra AI na firma. Na foto ainda está o esqueleto dela, que também montei. E com somente uma fonte.

Era o teste pra ver se ligava. E ligou.

Ontem fui instalar o sistema operacional, que será Ubuntu, e... cadê o nvme? Passei algum tempo pesquisando até que achei que é algo com a mother board, a ASUS Pro WS WRX80E-SAGE SE WIFI. O artigo que ajudou foi esse aqui:

https://forum.level1techs.com/t/solved-asus-pro-ws-wrx80e-sage-se-wifi-not-detecting-all-my-nvme-drives-in-proxmox/189373

tl;dr: é preciso passar o parâmetro pci=nommconf no boot pro kernel achar o nvme.

Quando estiver montada, provavelmente com metade das placas nvidia porque só recebemos 2 das 4 compradas, faço outro post com a foto e mais alguns dados.

Páginas de manual coloridas

Details
Written by: Helio Loureiro
Category: Linux
Published: December 01, 2025
Hits: 688
  • manpage

Essa é uma dica pra deixar a páginas do manual, vulgo man, coloridas.

É possível fazer em formato Bourne alike assim:

  
LESS_TERMCAP_mb=$(tput bold; tput setaf 2) # green
LESS_TERMCAP_md=$(tput bold; tput setaf 6) # cyan
LESS_TERMCAP_me=$(tput sgr0)
LESS_TERMCAP_so=$(tput bold; tput setaf 3; tput setab 4) # yellow on blue
LESS_TERMCAP_se=$(tput rmso; tput sgr0)
LESS_TERMCAP_us=$(tput smul; tput bold; tput setaf 7) # white
LESS_TERMCAP_ue=$(tput rmul; tput sgr0)
LESS_TERMCAP_mr=$(tput rev)
LESS_TERMCAP_mh=$(tput dim)
LESS_TERMCAP_ZN=$(tput ssubm)
LESS_TERMCAP_ZV=$(tput rsubm)
LESS_TERMCAP_ZO=$(tput ssupm)
LESS_TERMCAP_ZW=$(tput rsupm)
GROFF_NO_SGR=1

export LESS_TERMCAP_mb LESS_TERMCAP_md LESS_TERMCAP_me \
    LESS_TERMCAP_so LESS_TERMCAP_se LESS_TERMCAP_us \
    LESS_TERMCAP_ue LESS_TERMCAP_mr LESS_TERMCAP_mh \
    LESS_TERMCAP_ZN LESS_TERMCAP_ZV LESS_TERMCAP_ZO \
    LESS_TERMCAP_ZW GROFF_NO_SGR
  

Mas como uso fish, então adicionei as seguintes linhas em ~/.config/fish/conf.d/termcap.fish:

  
set -gx LESS_TERMCAP_mb (tput bold; tput setaf 2) # green
set -gx LESS_TERMCAP_md (tput bold; tput setaf 6) # cyan
set -gx LESS_TERMCAP_me (tput sgr0)
set -gx LESS_TERMCAP_so (tput bold; tput setaf 3; tput setab 4) # yellow on blue
set -gx LESS_TERMCAP_se (tput rmso; tput sgr0)
set -gx LESS_TERMCAP_us (tput smul; tput bold; tput setaf 7) # white
set -gx LESS_TERMCAP_ue (tput rmul; tput sgr0)
set -gx LESS_TERMCAP_mr (tput rev)
set -gx LESS_TERMCAP_mh (tput dim)
set -gx LESS_TERMCAP_ZN (tput ssubm)
set -gx LESS_TERMCAP_ZV (tput rsubm)
set -gx LESS_TERMCAP_ZO (tput ssupm)
set -gx LESS_TERMCAP_ZW (tput rsupm)
set -gx GROFF_NO_SGR 1    
  

Problema do ctrl+c no terminal

Details
Written by: Helio Loureiro
Category: Linux
Published: December 01, 2025
Hits: 615
  • Ubuntu
  • shell

Por algum motivo bizarro que não sei explicar, comecei a ter problema pra matar os programas rodando no terminal com ctrl+c.

  
❯ ping 10.4.6.101
PING 10.4.6.101 (10.4.6.101) 56(84) bytes of data.
^C^[^C^[^C^[^C^[^C^\^\^\^\

^C^C^Cfish: Job 1, 'ping 10.4.6.101' terminated by signal SIGKILL (Forced quit)    
  

Tentei de tudo: ctrl+alt+c, ctrl+alt+\, etc. Nenhum resultado adiantou e precisei sempre abrir outro terminal e mandar um kill no processo.

Mas hoje eu achei um artigo que corrigiu o problema:

https://unix.stackexchange.com/questions/18589/ctrlc-does-not-work-in-gnome-terminal

Então bastou um simples stty sane pra resolver de vez o problema.

  
❯ stty sane
❯ ping 10.4.6.101
PING 10.4.6.101 (10.4.6.101) 56(84) bytes of data.
^C
--- 10.4.6.101 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1014ms
  

E o ano do Linux no desktop continua com força total

Details
Written by: Helio Loureiro
Category: Linux
Published: November 16, 2025
Hits: 698
  • Linux desktop
  • Ano do Linux no desktop

E o progresso do Linux no desktop continua. Mais e mais canais no YouTube passaram a usar Linux pra uma ou outra coisa.

Se antes Linux era a coisa de nerd e geeks, agora virou mainstream.

Não que pudesse ser diferente. Basta ver o que é Android hoje em dia. Não é nicho. É mainstream.

Eu já comentei do canal do PewDiePie.

Passei a acompanhar também outro canal, o Switch and Click. Está uma delícia acompanhar as aventuras dela com Linux. De Mint ela já passou pra arch. E agora Omarchy.

E pra finalizar, o canal do Leon e da Nice. Esses, Windowzeros raizes. Mas vou dizer que a grande ajuda foi terem já migrado pro macOS. Isso "azeitou" a mudança ou uso em paralelo do Linux.

E por último, um último canal recomendado recentement no grupo Linux Brasil do Telegram. O cara instala Linux numa máquina super limitada e... funciona. Não que isso seja surpresa.

Monitorando a temperatura da sala dos servidores

Details
Written by: Helio Loureiro
Category: Linux
Published: September 12, 2025
Hits: 1177
  • grafana
  • alloy
  • temperature
  • temperatura

Por motivos que não cabem aqui, temos alguns servidores instalados em uma sala na empresa. E essa sala tem um ar-condicionado pra manter a temperatura sob controle.

Um belo dia estou olhando os dados no Grafana e noto que essas máquinas não reportaram dados (não temos alertas enviados pelo Grafana, mas o porquê disso fica pra um outro dia). Entro na sala e a temperatura estava simplemente... 32°C. Era verão na Suécia, que é curto mas tem seus dias bem quentes. E o hardware das máquinas desligaram pra proteção.

Entre entrar em contato com técnico do ar-condicionado e deixar a sala aberta pra ventilar, ficamos com aquele gosto amargo de não ter nenhum dado sobre a temperatura.

A solução? raspberrypi!

Ele tem um sensor que é vendido na Internet.

https://thepihut.com/products/temper2-usb-dual-temperature-sensor

O sensor já chegou mas não o raspberrypi. O motivo deve ser porque compramos um modelo que funciona como KVM e tem algumas coisas a mais.

Então aqui a descrição de como botar o serviço pra funcionar em Linux.

Dados via python

Existe um software descrito na página do produto que aponta pro seguinte repositório no GitHub:

https://github.com/modmypi/temper

Mas o repositório parece abandonado. Já faz 7 anos que ninguém manda nenhum commit. E o código não funciona com a versão mais moderna do sensor.

O que fazer? Patch!

Então corrigi o programa e criei um fork do repo original.

https://github.com/helioloureiro/temper

Então temos o software pronto pra funcionar. Ou quase.

Antes é preciso corrigir as permissões de leitura e escrita do dispositivo. E pra isso eu criei uma pequena regra no udev em /etc/udev/rules.d/90-temperature-sensor.rules

    
KERNEL=="hidraw[0-9]*", SUBSYSTEM=="hidraw", SUBSYSTEMS=="usb", ATTRS{idVendor}=="3553", ATTRS{idProduct}=="a001", MODE="0666", SYMLINK+="temper"      
    
  

Pegando a saída do kernel:

    
# dmesg | grep -i temper
[    5.152423] usb 1-10.4: Product: TEMPer2
[    6.769788] input: PCsensor TEMPer2 as /devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10.4/1-10.4:1.0/0003:3553:A001.0005/input/input14
[    6.826541] hid-generic 0003:3553:A001.0005: input,hidraw4: USB HID v1.11 Keyboard [PCsensor TEMPer2] on usb-0000:00:14.0-10.4/input0
[    6.826720] input: PCsensor TEMPer2 as /devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10.4/1-10.4:1.1/0003:3553:A001.0006/input/input15
[    6.827067] hid-generic 0003:3553:A001.0006: input,hidraw5: USB HID v1.10 Device [PCsensor TEMPer2] on usb-0000:00:14.0-10.4/input1
[  939.507362] usb 1-10.4: Product: TEMPer2
[  939.521617] input: PCsensor TEMPer2 as /devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10.4/1-10.4:1.0/0003:3553:A001.000B/input/input26
[  939.580825] hid-generic 0003:3553:A001.000B: input,hidraw4: USB HID v1.11 Keyboard [PCsensor TEMPer2] on usb-0000:00:14.0-10.4/input0
[  939.581808] input: PCsensor TEMPer2 as /devices/pci0000:00/0000:00:14.0/usb1/1-10/1-10.4/1-10.4:1.1/0003:3553:A001.000C/input/input27
[  939.582035] hid-generic 0003:3553:A001.000C: input,hidraw5: USB HID v1.10 Device [PCsensor TEMPer2] on usb-0000:00:14.0-10.4/input1
    
  

É possível ver que o dispositivo aparece como dois devices: /dev/hidraw4 e /dev/hidraw5.

Eu tentei usar a permissão 0644 primeiro, mas essa não funcionou pra ler os dados. Então tive de mudar pra 0666 mesmo sendo algo que só lê informação.

Feita essa etapa, ainda não estamos prontos pra rodar o programa temper.py. Ainda é preciso instalar a dependência: serial.

Se seu sistema é baseado em debian/ubuntu:

    
> sudo apt install -y python3-serial
    
  

Se não for, talvez seja mais fácil fazer com virtualenv. E pra isso eu atualmente uso o uv

   
> uv venv venv
> source venv/bin/activate     
(venv)> uv pip install serial
   
 

Tendo tudo pronto, chegamos ao momento da verdade:

   
(venv)> ./temper.py 
Bus 001 Dev 011 3553:a001 TEMPer2_V4.1 25.6C 78.0F - 22.8C 73.1F -
   
 

Pegando a saída como JSON permite ver melhor o que é cada um desses resultados.

   
(venv)>  ./temper.py --json
[
    {
        "vendorid": 13651,
        "productid": 40961,
        "manufacturer": "PCsensor",
        "product": "TEMPer2",
        "busnum": 1,
        "devnum": 11,
        "devices": [
            "hidraw4",
            "hidraw5"
        ],
        "firmware": "TEMPer2_V4.1",
        "hex_firmware": "54454d506572325f56342e3100000000",
        "hex_data": "808009f64e200000800108e94e200000",
        "internal temperature": 25.5,
        "external temperature": 22.81
    }
]     
   
 

Então a primeira temperatura lida, de /dev/hidraw4, é 25.5°C interna do dispositivo. A segunda, /dev/hidraw5, é de 22.81°C e externa, do cabo.

Temos as leituras e os dados. Como mandar isso pro Grafana?

Exportando as métricas pro Grafana

Eu primeiramente tentei fazer em shell script e mandar o dados pro mimir, que é onde eu agrego as métricas.

Fracassei miseravelmente.

Não existe uma forma muito fácil de enviar um dados pro lá. O formato que o alloy usa é protobuf, que é um dado comprimido em snappy, etc.

Qual outra alternativa?

Expor o dado como open metric pro alloy pegar e enviar.

Pode parecer simples mas... precisamos de um servidor web pra isso. Algo que temos fácil em python. Então usando uvicorn e fastapi podemos ter tudo funcionando. E é possível importar o temper.py como módulo.

E é preciso incrementar nosso virtualenv (ou pacotes) com esses pacotes:

  
> sudo apt install -y python3-uvicorn python3-fastapi
  
ou
  
(venv)> uv pip install uvicorn
(venv)> uv pip install fastapi
  

Sem mais delongas, eis aqui o código do monitor.py:

   
#! /usr/bin/env python3

import subprocess
import argparse
import logging
import threading
import time

try:
    import temper
except ImportError as e:
    print(f"Error importing temper module: {e}")
    print("Make sure python3-serial is installed: sudo apt-get install python3-serial")
    exit(1)

import uvicorn
from fastapi import FastAPI
from fastapi.responses import PlainTextResponse


CELSIUS = "\u2103"
DEFAULT_PORT = 8000
TEMPERATURE_MAX = 25.0

logger = logging.getLogger(__file__)
consoleOutputHandler = logging.StreamHandler()
formatter = logging.Formatter(
    fmt="[%(asctime)s] (%(levelname)s) %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S"
)
consoleOutputHandler.setFormatter(formatter)
logger.addHandler(consoleOutputHandler)
logger.setLevel(logging.INFO)

def shellExec(command: str) -> str:
    'run a command and return its output'
    try:
        return subprocess.getoutput(command)
    except Exception as e:
        logger.error(f"Error executing shell command '{command}': {e}")
        return f"Error: {e}"

app = FastAPI()

# Global temperature monitor instance (will be set in main)
temperature_monitor = None

@app.get("/metrics", response_class=PlainTextResponse)
async def metrics():
    if temperature_monitor is None or temperature_monitor.temperature_current is None:
        return ""
        
    temperature_current = temperature_monitor.temperature_current
    logger.info(f"/metrics: {temperature_current}{CELSIUS}")
    data_lines = list()
    data_lines.append("#HELP server_room_temperature_celsius the room with servers current temperature")
    data_lines.append("#TYPE server_room_temperature_celsius gauge")
    data_lines.append(f"server_room_temperature_celsius {temperature_current}")
    data_lines.append("")
    return "\n".join(data_lines)

class TemperatureMonitor:
    'A class that handle the temperature monitoring'
    port: int = DEFAULT_PORT
    temperature_max: float = TEMPERATURE_MAX
    temperature_current: float|None = None
    alert_lock: bool = False

    def __init__(self, port=None, temperature_max=None) -> None:
        if port:
            self.port = port
        if temperature_max:
            self.temperature_max = temperature_max

    def monitor(self) -> None:
        th = threading.Thread(target=self.webserver)
        th.daemon = True  # Make it a daemon thread
        th.start()
        try:
            while True:
                self.update()
                time.sleep(15)
        except KeyboardInterrupt:
            logger.info("Monitoring stopped by user")
        except Exception as e:
            logger.error(f"Error in monitoring loop: {e}")
            raise
     
    def webserver(self) -> None:
        uvicorn.run(app, host="127.0.0.1", port=self.port)

    def update(self) -> None:
        'Read the output from the command'
        try:
            tp = temper.Temper().read()
            if not tp or len(tp) == 0:
                logger.warning("No temperature devices found")
                self.temperature_current = None
                return
            
            self.temperature_current = tp[0].get('external temperature')
            if self.temperature_current is not None:
                logger.info(f"🌡️ current temperature: {self.temperature_current}{CELSIUS}")
            else:
                logger.warning("External temperature reading is None")
        except Exception as e:
            logger.error(f"Error reading temperature sensor: {e}")
            self.temperature_current = None

if __name__ == '__main__':
    parse = argparse.ArgumentParser(description="script to monitor temperature")
    parse.add_argument("--loglevel", default="info", help="the logging level (default=info)")
    parse.add_argument("--tempmax", type=float, default=TEMPERATURE_MAX, help="maximum temperature before raising alert")
    parse.add_argument("--port", type=int, default=DEFAULT_PORT, help="port to listen the service")
    args = parse.parse_args()

    # Validate log level
    valid_log_levels = ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']
    if args.loglevel.upper() not in valid_log_levels:
        print(f"Invalid log level: {args.loglevel}. Valid options: {', '.join(valid_log_levels)}")
        exit(1)
        
    if args.loglevel.upper() != "INFO":
        logger.setLevel(args.loglevel.upper())

    # Validate temperature threshold
    if args.tempmax <= 0:
        print("Temperature threshold must be greater than 0")
        exit(1)
        
    # Validate port number
    if not (1 <= args.port <= 65535):
        print("Port must be between 1 and 65535")
        exit(1)

    # Create temperature monitor instance
    temperature_monitor = TemperatureMonitor(args.port, args.tempmax)
    temperature_monitor.monitor()     
   
 

Eu tenho integrado uma parte de alerta que usa outro sistema, mas removi pra deixar o código fazendo somente o que é preciso.

No alloy, adicionei as seguintes linhas:

  
discovery.relabel "temperature_sensor" {
        targets = array.concat(
                [{
                __address__ = "localhost:8000",
                }],
        )

        rule {
                source_labels = ["__address__"]
                target_label  = "instance"
                replacement   = "temper"
        }
}

prometheus.scrape "temperature_sensor" {
        targets    = discovery.relabel.temperature_sensor.output
        forward_to = [prometheus.remote_write.prod.receiver]
        job_name   = "agent"
}    
  

Tudo pronto. Ou quase. Falta rodar o monitor.py que mostrei acima como serviço. E pra isso usamos o systemd. Basta criar o arquivo /etc/systemd/system/temperature-monitor.service e iniciar.

Serviço no systemd

  
[Unit]
Description=Temperature monitoring service
After=network.target

[Service]
User=helio
Group=helio
WorkingDirectory=/home/helio/temperature-sensor
ExecStart=/home/helio/temperature-sensor/monitor.sh
Restart=always

[Install]
WantedBy=multi-user.target    
  

O script monitor.sh é pra somente ler o virtualenv corretamente:

  
#! /usr/bin/env bash

die() {
	echo "ERROR: $@" >&2
	echo "[$(date)] exiting with error"
	exit 1
}

program="$0"
root_dir=$(readlink -f $program)
root_dir=$(dirname $root_dir)

cd $root_dir

source venv/bin/activate || \
    die "failed to read virtualenv"
exec ./monitor.py    
  

E iniciando o serviço:

  
> sudo systemctl daemon-reload
> sudo systemctl enable --now temperature-monitor    
  

O que resta é criar um gráfico pra métrica server_room_temperature_celsius e partir pro abraço.

Update: [Fri Sep 12 05:30:00 PM CEST 2025] acabo de perceber que o repositório que fiz fork é na verdade um fork de outro, que parece ser bem mais completo.

https://github.com/urwen/temper
  1. Problema com zfs no Ubuntu
  2. O ano do Linux no desktop está acontecendo!
  3. Autenticação por reconhecimento biométrico (leitura de digital)
  4. Configurando o teclado Keychron C3 no Linux

Page 2 of 22

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Estatísticas

  • Users 2
  • Articles 509
  • Articles View Hits 3654463

Imagem aleatória