Acabei tendo problemas de memória no laptop de trabalho. Então resolvi aumentar o tamanho do swap. Ou melhor, adicionar um volume lógico no zfs pro swap.
Após uma pesquisa rápida, achei a forma e fazer isso. Sem grandes surpresas, extremamente fácil.
root@silverhand$ zfs create -V 64G \
-b (getconf PAGESIZE) \
-o compression=zstd-fast \
-o logbias=throughput \
-o sync=always -o primarycache=metadata \
-o secondarycache=none \
-o com.sun:auto-snapshot=false \
rpool/swap
root@silverhand$ mkswap /dev/zvol/rpool/swap
root@silverhand$ swapon /dev/zvol/rpool/swap
O resultado foi rápido e fácil. Com 32 GB de RAM eu não esperava precisar tanto assim de swap. Mas o chrome insiste em tomar toda memória livre possível.
Não necessariamente relacionado, mas fiz upgrade do meu laptop pro Ubuntu 24.10. Como também está com zfs, fiz snapshots de cada volume antes de atualizar. Então fica fácil voltar se algo der errado. Por enquanto parece estar funcionando como esperado. E devo escrever sobre isso em breve.
Esse é um update to artigo usando python pra capturar a webcam. E com o uso do obamawatched 2021. Eu atualizei recentemente o obamawatcher pra rodar com PySide6 e está aqui funcionando no meu laptop atual de trabalho. No meu pessoal também. Ao menos acho que está funcionando.
O resultado final já está no vídeo acima. Como são várias imagens, os comandos que listei antes já não funcionam muito bem. Eu precisei primeiro redimensionar as imagens pra 640x360 para ficar em widescreen (16:9). As fotos mais antigas saíram em formato quadrado porque era o que a Webcam suportava na época. Então precisei cortar pra ficarem no aspecto correto. E pra isso eu usei python com pillow.
#! /usr/bin/env python3
import os
import re
import argparse
try:
from PIL import Image
except ModuleNotFoundError as e:
raise Exception("missing pillow - run: pip install Pillow") from e
golden_rate = 1280/720
default_size_x = 640
default_size_y = 360
parse = argparse.ArgumentParser(description="Script to resize pictures from a specific directory to the same size")
parse.add_argument("--directory", required=True, help="directory with images jpg")
args = parse.parse_args()
for filename in sorted(os.listdir(args.directory)):
if not re.search("jpg", filename):
continue
with Image.open(args.directory + "/" + filename) as image:
width, height = image.size
rate = float(width)/float(height)
is_golden = rate == golden_rate
is_correted = False
rate_from_default = width / default_size_x
if rate_from_default == 1:
pass
elif rate_from_default > 1:
image = image.resize((default_size_x, int(height/rate_from_default)))
is_correted = True
else:
image = image.resize((default_size_x, int(height/rate_from_default)), Image.Resampling.LANCZOS)
is_correted = True
if not is_golden:
image = image.crop((0, 0, default_size_x, default_size_y))
image.save(filename)
print(f"{filename}: {width}x{height}, golden: {is_golden}, corrected: {is_correted}")
print(f"Golden rate: {golden_rate}")
Tendo as imagens no mesmo formato, basta ordernar e usar ffmpeg pra montar o vídeo.
#! /usr/bin/env bash
die() {
echo "$1" >&2
exit 1
}
counter=0
for img in [0-9]*.jpg
do
serial=$(printf "%06d" $counter)
new_name="G${serial}.JPG"
counter=$(expr $counter + 1)
if [ -f "$new_name" ]; then
echo "$new_name already exists"
continue
fi
echo "$img => $new_name"
mv $img $new_name
done
case $(uname -s) in
Linux)
echo "Merging images into single video file: output.mp4"
ffmpeg -r 8 -i G%06d.JPG -c:v h264 -b:v 5M output.mp4 || \
die "Failed to render output.mp4"
;;
Darwin)
echo "Merging images into single video file: output.mp4"
ffmpeg -hwaccel auto -r 8 -i G%06d.JPG -c:v h264_videotoolbox -b:v 5M output.mp4 || \
die "Failed to render output.mp4"
esac
Eu não testei o código de macOS, então pode ser que não funcione. Meu PC atual, um Lenovo Thinkpad, é processador e GPU Intel. Eu tentei usar -hwaccel vaapi
mas as cores saiam erradas, no estilo negativo de filme. Então deixei rodar no processador mesmo.
E pra comparar o resultado em mpeg comparado com o mesmo em gif:
░▒▓ …/tmp/imagens-webcam/Webcam2 via v3.12.3 (venv) 15:33
❯ gm convert -delay 20 *JPG output.gif
░▒▓ …/tmp/imagens-webcam/Webcam2 via v3.12.3 (venv) 15:38
❯ ls -l output.*
.rw-rw-r-- 209M helio 27 Nov 15:38 output.gif
.rw-rw-r-- 103M helio 27 Nov 13:54 output.mp4
A geração do gif demorou uma eternidade, 5 minutos. O ffmpeg foram só alguns segundos. O tamanho foi o dobro no gif. Pra ver a imagem, teria de carregar tudo e só depois ver o resultando. Como mpeg, vai enviando o vídeo aos poucos.
Eu finalizei o vídeo no kdenlive, fazendo o merge com o vídeo anterior e adicionando a música. Ficou uma nostalgia gostosa.
E com certeza atualizarei daqui alguns anos.
Abri um grupo novo, SRE - Site Reliability Engineer, e uma categoria nova sobre Ansible já que agora esse é meu mundo. Estou trabalhando nessa área mantendo servidores rodando na empresa nova.
Eu estou migrando um sistema que cria VMs de python, pela falta de suporte na biblioteca, pra outra forma via linha de comando. Mas essa parte não interessa muito. O interessante é sobre como fiz parte do trabalho.
E estou escrevendo a respeito porque foi difícil achar informação sobre isso na Internet. A maioria estava ou errada ou não funcionava como esperado.
Pra começar, vamos dar uma olhada na estrutura de dados que recebo do programa. Pra simular o mesmo no Ansible, vou trocar o comando por um: cat server.json
(onde server.json contém esse dado). Meu objetivo aqui é pegar o IPv4 da interface pública pra depois provisionar o sistema com os programas que vamos usar.
{
"core_number": "1",
"hostname": "helio-testing.loureiro.eng.br",
"license": 0,
"memory_amount": "1024",
"plan": "1xCPU-1GB",
"progress": "0",
"state": "started",
"tags": [],
"title": "helio-testing.loureiro.eng.br (created via ansible)",
"uuid": "87271929-a137-4910-9648-75d05b6d3ecb",
"zone": "sweden-stockholm",
"boot_order": "disk",
"firewall": "off",
"host": 12345678,
"ip_addresses": [
{
"access": "internal",
"address": "192.168.0.1",
"family": "IPv4",
"part_of_plan": "no",
"ptr_record": "",
"server": "",
"mac": "",
"floating": "no",
"zone": ""
},
{
"access": "public",
"address": "1.2.3.4",
"family": "IPv4",
"part_of_plan": "yes",
"ptr_record": "",
"server": "",
"mac": "",
"floating": "no",
"zone": ""
}
],
"labels": {
"label": []
},
"metadata": "yes",
"nic_model": "virtio",
"networking": {
"interfaces": [
{
"index": 1,
"ip_addresses": [
{
"access": "",
"address": "1.2.3.4",
"family": "IPv4",
"part_of_plan": "no",
"ptr_record": "",
"server": "",
"mac": "",
"floating": "no",
"zone": ""
}
],
"mac": "a1:b2:c3:e4:f5:01",
"network": "fb29cb51-5dd7-4674-baac-b0253a725305",
"type": "public",
"bootable": "no",
"source_ip_filtering": "yes"
},
{
"index": 2,
"ip_addresses": [
{
"access": "",
"address": "192.168.0.1",
"family": "IPv4",
"part_of_plan": "no",
"ptr_record": "",
"server": "",
"mac": "",
"floating": "no",
"zone": ""
}
],
"mac": "a1:b2:c3:e4:f5:02",
"network": "92351420-66ec-46e9-98ee-149e3a588bdf",
"type": "utility",
"bootable": "no",
"source_ip_filtering": "yes"
}
]
},
"server_group": "",
"simple_backup": "no",
"storage_devices": [
{
"address": "virtio:0",
"storage": "fe128b6b-876c-4f7b-a070-3a8d6cec6159",
"storage_size": 10,
"storage_tier": "maxiops",
"storage_title": "helio-testing.loureiro.eng.br-OS",
"type": "disk",
"boot_disk": "0"
}
],
"timezone": "UTC",
"video_model": "vga",
"remote_access_enabled": "no",
"remote_access_type": "vnc",
"remote_access_host": "",
"remote_access_password": "abracadabra",
"remote_access_port": "0"
}
Então vamos começar com como pegar a saída desse comando no ansible. Pra isso basta chamar uma parte simples que registra o resultado.
---
- debug: msg="running the command and getting json as result"
- name: read json file
command: >
cat /tmp/server.json
register: result
failed_when: result.rc != 0
- debug:
msg="{{ result }}"
Não tem muito segredo aqui. Eu rodo o comando, que aqui eu troquei pelo cat
e recebo o resultando em result
. Eu olho se result.rc
é diferente de zero pra saber se deu certo (não zero significa erro).
- name: parsing as json
set_fact:
json_result: "{{ result.stdout }}"
- debug:
msg=" from json_result "
msg="{{ json_result}}"
Aqui foi o primeiro pulo do gato. Pra receber o dado como json bastava só pegar de result.stdout
. Em muitos lugares achei receitas exotéricas como {{ result | to_json }}
. Todas erradas.
- name: getting IPs
set_fact:
json_ip_addresses: "{{ json_result.ip_addresses }}"
- debug: msg="{{ json_ip_addresses }}"
Do código json que recebo, eu filtro somente ip_addresses
, que é a parte que me interessa.
- name: filtering public IPs
set_fact:
public_ip_addresses: "{{ json_ip_addresses | json_query(query) }}"
vars:
query: "[?access=='public']"
- debug: msg="{{ public_ip_addresses }}"
Em seguida eu aplico filtro de json usando a sintaxe de jpath. Na parte da query, que tem um array, eu quero somente aquela que tem a propriedade "access" com o valor "public". A query vai dentro de vars
por causa das aspas simples e duplas.
- name: filtering IPv4 only
set_fact:
public_ipv4_address: "{{ public_ip_addresses | json_query(query) }}"
vars:
query: "[?family=='IPv4'].address"
- debug: msg="{{ public_ipv4_address }}"
No exemplo que tenho eu já não tenho mais nada, mas em alguns casos eu tenho um array com 2 elementos: IPv4 e IPv6. Então essa segunda query é pra filtrar qual estrutura tem a propriedade "family" com valor "IPv4". Dessa estrutura eu quero o valor de "address". E pronto! Está aí o IPv4 filtrado. O valor é 1.2.3.4, que é o esperado.
Agora rodando com Ansbile:
❯ ansible-playbook deploy.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'
PLAY [get JSON] ***************************************************************************************************************************************************************************
TASK [getjson : debug] ********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "running from roles/getjson/tasks"
}
TASK [getjson : read json file] ***********************************************************************************************************************************************************
changed: [localhost]
TASK [getjson : debug] ********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"changed": true,
"cmd": [
"cat",
"../server.json"
],
"delta": "0:00:00.002636",
"end": "2024-11-23 16:02:30.099466",
"failed": false,
"failed_when_result": false,
"msg": "",
"rc": 0,
"start": "2024-11-23 16:02:30.096830",
"stderr": "",
"stderr_lines": [],
"stdout": "{\n \"core_number\": \"1\",\n \"hostname\": \"helio-testing.loureiro.eng.br\",\n \"license\": 0,\n \"memory_amount\": \"1024\",\n \"plan\": \"1xCPU-1GB\",\n \"progress\": \"0\",\n \"state\": \"started\",\n \"tags\": [],\n \"title\": \"helio-testing.loureiro.eng.br (created via ansible)\",\n \"uuid\": \"87271929-a137-4910-9648-75d05b6d3ecb\",\n \"zone\": \"sweden-stockholm\",\n \"boot_order\": \"disk\",\n \"firewall\": \"off\",\n \"host\": 12345678,\n \"ip_addresses\": [\n {\n \"access\": \"internal\",\n \"address\": \"192.168.0.1\",\n \"family\": \"IPv4\",\n \"part_of_plan\": \"no\",\n \"ptr_record\": \"\",\n \"server\": \"\",\n \"mac\": \"\",\n \"floating\": \"no\",\n \"zone\": \"\"\n },\n {\n \"access\": \"public\",\n \"address\": \"1.2.3.4\",\n \"family\": \"IPv4\",\n \"part_of_plan\": \"yes\",\n \"ptr_record\": \"\",\n \"server\": \"\",\n \"mac\": \"\",\n \"floating\": \"no\",\n \"zone\": \"\"\n }\n ],\n \"labels\": {\n \"label\": []\n },\n \"metadata\": \"yes\",\n \"nic_model\": \"virtio\",\n \"networking\": {\n \"interfaces\": [\n {\n \"index\": 1,\n \"ip_addresses\": [\n {\n \"access\": \"\",\n \"address\": \"1.2.3.4\",\n \"family\": \"IPv4\",\n \"part_of_plan\": \"no\",\n \"ptr_record\": \"\",\n \"server\": \"\",\n \"mac\": \"\",\n \"floating\": \"no\",\n \"zone\": \"\"\n }\n ],\n \"mac\": \"a1:b2:c3:e4:f5:01\",\n \"network\": \"fb29cb51-5dd7-4674-baac-b0253a725305\",\n \"type\": \"public\",\n \"bootable\": \"no\",\n \"source_ip_filtering\": \"yes\"\n },\n {\n \"index\": 2,\n \"ip_addresses\": [\n {\n \"access\": \"\",\n \"address\": \"192.168.0.1\",\n \"family\": \"IPv4\",\n \"part_of_plan\": \"no\",\n \"ptr_record\": \"\",\n \"server\": \"\",\n \"mac\": \"\",\n \"floating\": \"no\",\n \"zone\": \"\"\n }\n ],\n \"mac\": \"a1:b2:c3:e4:f5:02\",\n \"network\": \"92351420-66ec-46e9-98ee-149e3a588bdf\",\n \"type\": \"utility\",\n \"bootable\": \"no\",\n \"source_ip_filtering\": \"yes\"\n }\n ]\n },\n \"server_group\": \"\",\n \"simple_backup\": \"no\",\n \"storage_devices\": [\n {\n \"address\": \"virtio:0\",\n \"storage_encrypted\": \"yes\",\n \"part_of_plan\": \"\",\n \"storage\": \"fe128b6b-876c-4f7b-a070-3a8d6cec6159\",\n \"storage_size\": 50,\n \"storage_tier\": \"maxiops\",\n \"storage_title\": \"helio-testing.loureiro.eng.br-OS\",\n \"type\": \"disk\",\n \"boot_disk\": \"0\"\n }\n ],\n \"timezone\": \"UTC\",\n \"video_model\": \"vga\",\n \"remote_access_enabled\": \"no\",\n \"remote_access_type\": \"vnc\",\n \"remote_access_host\": \"\",\n \"remote_access_password\": \"abracadabra\",\n \"remote_access_port\": \"0\"\n}",
"stdout_lines": [
"{",
" \"core_number\": \"1\",",
" \"hostname\": \"helio-testing.loureiro.eng.br\",",
" \"license\": 0,",
" \"memory_amount\": \"1024\",",
" \"plan\": \"1xCPU-1GB\",",
" \"progress\": \"0\",",
" \"state\": \"started\",",
" \"tags\": [],",
" \"title\": \"helio-testing.loureiro.eng.br (created via ansible)\",",
" \"uuid\": \"87271929-a137-4910-9648-75d05b6d3ecb\",",
" \"zone\": \"sweden-stockholm\",",
" \"boot_order\": \"disk\",",
" \"firewall\": \"off\",",
" \"host\": 12345678,",
" \"ip_addresses\": [",
" {",
" \"access\": \"internal\",",
" \"address\": \"192.168.0.1\",",
" \"family\": \"IPv4\",",
" \"part_of_plan\": \"no\",",
" \"ptr_record\": \"\",",
" \"server\": \"\",",
" \"mac\": \"\",",
" \"floating\": \"no\",",
" \"zone\": \"\"",
" },",
" {",
" \"access\": \"public\",",
" \"address\": \"1.2.3.4\",",
" \"family\": \"IPv4\",",
" \"part_of_plan\": \"yes\",",
" \"ptr_record\": \"\",",
" \"server\": \"\",",
" \"mac\": \"\",",
" \"floating\": \"no\",",
" \"zone\": \"\"",
" }",
" ],",
" \"labels\": {",
" \"label\": []",
" },",
" \"metadata\": \"yes\",",
" \"nic_model\": \"virtio\",",
" \"networking\": {",
" \"interfaces\": [",
" {",
" \"index\": 1,",
" \"ip_addresses\": [",
" {",
" \"access\": \"\",",
" \"address\": \"1.2.3.4\",",
" \"family\": \"IPv4\",",
" \"part_of_plan\": \"no\",",
" \"ptr_record\": \"\",",
" \"server\": \"\",",
" \"mac\": \"\",",
" \"floating\": \"no\",",
" \"zone\": \"\"",
" }",
" ],",
" \"mac\": \"a1:b2:c3:e4:f5:01\",",
" \"network\": \"fb29cb51-5dd7-4674-baac-b0253a725305\",",
" \"type\": \"public\",",
" \"bootable\": \"no\",",
" \"source_ip_filtering\": \"yes\"",
" },",
" {",
" \"index\": 2,",
" \"ip_addresses\": [",
" {",
" \"access\": \"\",",
" \"address\": \"192.168.0.1\",",
" \"family\": \"IPv4\",",
" \"part_of_plan\": \"no\",",
" \"ptr_record\": \"\",",
" \"server\": \"\",",
" \"mac\": \"\",",
" \"floating\": \"no\",",
" \"zone\": \"\"",
" }",
" ],",
" \"mac\": \"a1:b2:c3:e4:f5:02\",",
" \"network\": \"92351420-66ec-46e9-98ee-149e3a588bdf\",",
" \"type\": \"utility\",",
" \"bootable\": \"no\",",
" \"source_ip_filtering\": \"yes\"",
" }",
" ]",
" },",
" \"server_group\": \"\",",
" \"simple_backup\": \"no\",",
" \"storage_devices\": [",
" {",
" \"address\": \"virtio:0\",",
" \"storage_encrypted\": \"yes\",",
" \"part_of_plan\": \"\",",
" \"storage\": \"fe128b6b-876c-4f7b-a070-3a8d6cec6159\",",
" \"storage_size\": 50,",
" \"storage_tier\": \"maxiops\",",
" \"storage_title\": \"helio-testing.loureiro.eng.br-OS\",",
" \"type\": \"disk\",",
" \"boot_disk\": \"0\"",
" }",
" ],",
" \"timezone\": \"UTC\",",
" \"video_model\": \"vga\",",
" \"remote_access_enabled\": \"no\",",
" \"remote_access_type\": \"vnc\",",
" \"remote_access_host\": \"\",",
" \"remote_access_password\": \"abracadabra\",",
" \"remote_access_port\": \"0\"",
"}"
]
}
}
TASK [getjson : parsing as json] **********************************************************************************************************************************************************
ok: [localhost]
TASK [getjson : debug] ********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": {
"boot_order": "disk",
"core_number": "1",
"firewall": "off",
"host": 12345678,
"hostname": "helio-testing.loureiro.eng.br",
"ip_addresses": [
{
"access": "internal",
"address": "192.168.0.1",
"family": "IPv4",
"floating": "no",
"mac": "",
"part_of_plan": "no",
"ptr_record": "",
"server": "",
"zone": ""
},
{
"access": "public",
"address": "1.2.3.4",
"family": "IPv4",
"floating": "no",
"mac": "",
"part_of_plan": "yes",
"ptr_record": "",
"server": "",
"zone": ""
}
],
"labels": {
"label": []
},
"license": 0,
"memory_amount": "1024",
"metadata": "yes",
"networking": {
"interfaces": [
{
"bootable": "no",
"index": 1,
"ip_addresses": [
{
"access": "",
"address": "1.2.3.4",
"family": "IPv4",
"floating": "no",
"mac": "",
"part_of_plan": "no",
"ptr_record": "",
"server": "",
"zone": ""
}
],
"mac": "a1:b2:c3:e4:f5:01",
"network": "fb29cb51-5dd7-4674-baac-b0253a725305",
"source_ip_filtering": "yes",
"type": "public"
},
{
"bootable": "no",
"index": 2,
"ip_addresses": [
{
"access": "",
"address": "192.168.0.1",
"family": "IPv4",
"floating": "no",
"mac": "",
"part_of_plan": "no",
"ptr_record": "",
"server": "",
"zone": ""
}
],
"mac": "a1:b2:c3:e4:f5:02",
"network": "92351420-66ec-46e9-98ee-149e3a588bdf",
"source_ip_filtering": "yes",
"type": "utility"
}
]
},
"nic_model": "virtio",
"plan": "1xCPU-1GB",
"progress": "0",
"remote_access_enabled": "no",
"remote_access_host": "",
"remote_access_password": "abracadabra",
"remote_access_port": "0",
"remote_access_type": "vnc",
"server_group": "",
"simple_backup": "no",
"state": "started",
"storage_devices": [
{
"address": "virtio:0",
"boot_disk": "0",
"part_of_plan": "",
"storage": "fe128b6b-876c-4f7b-a070-3a8d6cec6159",
"storage_encrypted": "yes",
"storage_size": 50,
"storage_tier": "maxiops",
"storage_title": "helio-testing.loureiro.eng.br-OS",
"type": "disk"
}
],
"tags": [],
"timezone": "UTC",
"title": "helio-testing.loureiro.eng.br (created via ansible)",
"uuid": "87271929-a137-4910-9648-75d05b6d3ecb",
"video_model": "vga",
"zone": "sweden-stockholm"
}
}
TASK [getjson : getting IPs] **************************************************************************************************************************************************************
ok: [localhost]
TASK [getjson : debug] ********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"access": "internal",
"address": "192.168.0.1",
"family": "IPv4",
"floating": "no",
"mac": "",
"part_of_plan": "no",
"ptr_record": "",
"server": "",
"zone": ""
},
{
"access": "public",
"address": "1.2.3.4",
"family": "IPv4",
"floating": "no",
"mac": "",
"part_of_plan": "yes",
"ptr_record": "",
"server": "",
"zone": ""
}
]
}
TASK [getjson : filtering public IPs] *****************************************************************************************************************************************************
ok: [localhost]
TASK [getjson : debug] ********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
{
"access": "public",
"address": "1.2.3.4",
"family": "IPv4",
"floating": "no",
"mac": "",
"part_of_plan": "yes",
"ptr_record": "",
"server": "",
"zone": ""
}
]
}
TASK [getjson : filtering IPv4 only] ******************************************************************************************************************************************************
ok: [localhost]
TASK [getjson : debug] ********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": [
"1.2.3.4"
]
}
TASK [getjson : cloud server IP] ********************************************************************************************************************************************************
ok: [localhost]
TASK [getjson : debug] ********************************************************************************************************************************************************************
ok: [localhost] => {
"msg": "1.2.3.4"
}
PLAY RECAP *********************************************************************************************************************************************************************************
localhost : ok=15 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Bom ansible and happy coding 😄
Esse é um código que estamos usando bastante aqui na firma nova. Ajuda a fazer seu script decidir se segue em frente ou para. E basta apenas apertar uma teclar, sem enter.
#! /usr/bin/env bash
read -p "Deseja continuar (s/n)? " -n 1 -r resposta
if [[ "$resposta" =~ [sS] ]];then
echo "Resposta foi sim"
else
echo "Resposta foi não"
fi
Um projeto engraçado no GitHub que serve pra te detonar baseado nos seus repositórios. Estou rindo, mas é de nervoso. É um tipo de Mauchine Learning.
Oh Helio Loureiro, trying to brew your own beer and code at the same time? I can only imagine the taste of your programming—bitter, flat, and with a hint of regret, just like your idea of collaborating on KDE while you’re still figuring out how to stop your Python scripts from crashing. With a profile that screams “I’m learning” louder than a Linux fanboy at a Windows convention, maybe it’s time to put down the homebrew and pick up a book on actual development skills. Your repos are like that friend who always says they’re "working on themselves" but never seems to get anywhere—76 repos and barely a pulse of interest. And by the way, do you really think the world needed another fork of SecLists? Your GitHub looks like a garage sale of uninteresting projects. But hey, at least you’re keeping Linux alive, one mediocre script at a time. Cheers to that!
O que se pode ler mais ou menos como:
Ah, Helio Loureiro, tentando fazer sua própria cerveja e programar ao mesmo tempo? Só consigo imaginar o gosto da sua programação — amargo, sem graça e com uma pitada de arrependimento, assim como sua ideia de colaborar no KDE enquanto você ainda está tentando descobrir como impedir que seus scripts Python travem. Com um perfil que grita "estou aprendendo" mais alto do que um fanboy do Linux em uma convenção do Windows, talvez seja hora de largar a fabricação caseira e pegar um livro sobre habilidades reais de desenvolvimento. Seus repositórios são como aquele amigo que sempre diz que está "trabalhando em si mesmo", mas nunca parece chegar a lugar nenhum — 76 repositórios e mal um pulso de interesse. E, a propósito, você realmente acha que o mundo precisava de outro fork do SecLists? Seu GitHub parece uma venda de garagem de projetos desinteressantes. Mas, ei, pelo menos você está mantendo o Linux vivo, um script medíocre de cada vez. Um brinde a isso!
Quem quiser experimentar a brincadeira, esse é o link:
Estou atualmente trabalhando numa empresa onde uma das visões da empresa é conseguir ter os dados do jogo finalizado disponível na interface de visualização em menos de 1 minuto. Quase todo o código é escrito em typescript e uma pequena parte em rust.
Uma das partes mais pesadas é feita em shell, que é a parte de baixar e descompactar um arquivo da steam. Pra ilustrar o que é feito, fiz esse script em shell que também serve como base de comparação de tempo.
#! /usr/bin/env bash
#
TARGET_URL="http://replay272.valve.net/730/003705744548740202576_0842061407.dem.bz2"
DESTINATION="003705744548740202576_0842061407.dem.bz2"
UNPACKED="003705744548740202576_0842061407.dem"
CURLUNPACKED="curl-003705744548740202576_0842061407.dem"
die() {
echo "ERROR: $@" &>2
}
rm -f $DESTINATION $UNPACKED $CURLUNPACKED
curl -o $DESTINATION \
-L \
$TARGET_URL ||
die "Failed to download: $TARGET_URL"
bunzip2 $DESTINATION ||
die "Failed to unzip $DESTINATION"
mv $UNPACKED $CURLUNPACKED
O código então baixa esse link da steam e tem de descompactar o arquivo. Qual é a velocidade dele?
helio@goosfraba ~/t/godownloader> time ./curl-downloader-2.sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 216M 100 216M 0 0 60.3M 0 0:00:03 0:00:03 --:--:-- 60.4M
________________________________________________________
Executed in 29.61 secs fish external
usr time 25.24 secs 585.00 micros 25.24 secs
sys time 0.89 secs 71.00 micros 0.89 secs
Podemos ver que o "curl" baixa o arquivo em mais ou menos 3 segundos, e gasta no total 25s, ou seja, uns 22s pra descompactar. O total termina como 29s, mas vamos focar primeiramente no "usr time".
Então escrevi um código em Go pra tentar fazer isso de forma mais rápida.
package main
import (
"compress/bzip2"
"fmt"
"io"
"net/http"
"os"
"time"
)
const (
TARGET_URL = "http://replay272.valve.net/730/003705744548740202576_0842061407.dem.bz2"
DESTINATION = "003705744548740202576_0842061407.dem.bz2"
DECOMPRESSED = "go-003705744548740202576_0842061407.dem"
)
func main() {
err := download(TARGET_URL, DESTINATION)
if err != nil {
panic(err)
}
err = bunzip2(DESTINATION, DECOMPRESSED)
if err != nil {
panic(err)
}
}
func download(from, to string) error {
fmt.Println("Downloading:", from)
timeStart := time.Now()
resp, err := http.Get(from)
if err != nil {
return err
}
defer resp.Body.Close()
out, err := os.Create(to)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
if err != nil {
return err
}
fmt.Println("Finished:", time.Since(timeStart).String())
return nil
}
func bunzip2(from, to string) error {
// https://gist.github.com/rickt/7817401
fmt.Println("Unpacking:", from)
timeStart := time.Now()
pr, pw := io.Pipe()
go func() {
defer pw.Close()
var inFile *os.File
var err error
inFile, err = os.Open(from)
defer inFile.Close()
if err != nil {
panic(err)
}
_, err = io.Copy(pw, inFile)
if err != nil {
panic(err)
}
}()
defer pr.Close()
z := bzip2.NewReader(pr)
var outFile *os.File
var err error
outFile, err = os.Create(to)
defer outFile.Close()
if err != nil {
return err
}
_, err = io.Copy(outFile, z)
if err != nil {
return err
}
fmt.Println("Finished:", time.Since(timeStart).String())
return nil
}
O código baixa o conteúdo num arquivo e depois descompacta. Qual a velocidade?
helio@goosfraba ~/t/godownloader> go build -o go-downloader-2 main-2.go; time ./go-downloader-2
Downloading: http://replay272.valve.net/730/003705744548740202576_0842061407.dem.bz2
Finished: 3.536812275s
Unpacking: 003705744548740202576_0842061407.dem.bz2
Finished: 30.220663099s
________________________________________________________
Executed in 33.76 secs fish external
usr time 29.90 secs 0.00 micros 29.90 secs
sys time 0.95 secs 639.00 micros 0.95 secs
Muito lento. Pior do que eu esperava. Ele baixa o arquivo em 3.5s, mas leva 30s pra descompactar. Um dos motivos é com certeza porquê eu salvo em arquivo e depois abro o arquivo pra descompactar. Vamos então pra próxima versão onde eu passo io.Reader de um pro outro.
package main
import (
"compress/bzip2"
"fmt"
"io"
"net/http"
"os"
"time"
)
const (
TARGET_URL = "http://replay272.valve.net/730/003705744548740202576_0842061407.dem.bz2"
DESTINATION = "003705744548740202576_0842061407.dem.bz2"
DECOMPRESSED = "go-003705744548740202576_0842061407.dem"
)
func main() {
data, err := readerDownload(TARGET_URL, DESTINATION)
if err != nil {
panic(err)
}
err = bunzip2Stream(data, DECOMPRESSED)
if err != nil {
panic(err)
}
}
func readerDownload(from, to string) ([]byte, error) {
fmt.Println("Downloading:", from)
timeStart := time.Now()
resp, err := http.Get(from)
if err != nil {
return nil, err
}
defer resp.Body.Close()
content, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println("Finished:", time.Since(timeStart).String())
return content, nil
}
func bunzip2Stream(from []byte, to string) error {
// https://gist.github.com/rickt/7817401
fmt.Println("Unpacking:", to)
timeStart := time.Now()
pr, pw := io.Pipe()
go func() {
defer pw.Close()
//_, err := io.Copy(pw, from)
pw.Write(from)
//if err != nil {
// panic(err)
// }
}()
defer pr.Close()
z := bzip2.NewReader(pr)
var outFile *os.File
var err error
outFile, err = os.Create(to)
defer outFile.Close()
if err != nil {
return err
}
_, err = io.Copy(outFile, z)
if err != nil {
return err
}
fmt.Println("Finished:", time.Since(timeStart).String())
return nil
}
Qual o desempenho?
helio@goosfraba ~/t/godownloader> go build -o go-downloader-3 main-3.go; time ./go-downloader-3
Downloading: http://replay272.valve.net/730/003705744548740202576_0842061407.dem.bz2
Finished: 3.624793323s
Unpacking: go-003705744548740202576_0842061407.dem
Finished: 29.883794408s
________________________________________________________
Executed in 33.52 secs fish external
usr time 30.03 secs 580.00 micros 30.03 secs
sys time 0.96 secs 69.00 micros 0.96 secs
Melhorou mas ainda estou longe de fazer melhor que a versão em shell script. O tempo de descompactar baixou irrisóriamente 1s. Mesmo não tendo SSD ou NVME meu disco é rápido o suficiente pra isso não impactar ao todo.
Comecei a pesquisar como poderia melhorar o desempanho do pacote bzip2 do Go! e existe uma discussão sobre isso em aberto.
https://github.com/golang/go/issues/6754
A reclamação é sobre versões mais antigas de Go! e até Robert Pike opinia. Acho que melhorou bastante pros dias de hoje, mas continua lento se comparado com o binário do programa. O que fazer então? Declarar derrota?
Talvez. Mas ao invés disso eu passei a procurar outras libs no GitHub. E encontrei o uso do pbzip2, que diz ser mais rápido queo bzip2 do standard. Então vamos ao código:
package main
import (
"context"
"fmt"
"io"
"net/http"
"os"
"time"
"github.com/cosnicolaou/pbzip2"
)
const (
TARGET_URL = "http://replay272.valve.net/730/003705744548740202576_0842061407.dem.bz2"
DESTINATION = "003705744548740202576_0842061407.dem.bz2"
DECOMPRESSED = "go-003705744548740202576_0842061407.dem"
)
func main() {
data, err := readerDownload(TARGET_URL, DESTINATION)
if err != nil {
panic(err)
}
err = pbunzip2Stream(data, DECOMPRESSED)
if err != nil {
panic(err)
}
}
func readerDownload(from, to string) ([]byte, error) {
fmt.Println("Downloading:", from)
timeStart := time.Now()
resp, err := http.Get(from)
if err != nil {
return nil, err
}
defer resp.Body.Close()
content, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
fmt.Println("Finished:", time.Since(timeStart).String())
return content, nil
}
func pbunzip2Stream(from []byte, to string) error {
// https://gist.github.com/rickt/7817401
fmt.Println("Unpacking:", to)
timeStart := time.Now()
pr, pw := io.Pipe()
go func() {
defer pw.Close()
//_, err := io.Copy(pw, from)
pw.Write(from)
//if err != nil {
// panic(err)
// }
}()
defer pr.Close()
ctx := context.Background()
z := pbzip2.NewReader(ctx, pr)
var outFile *os.File
var err error
outFile, err = os.Create(to)
defer outFile.Close()
if err != nil {
return err
}
_, err = io.Copy(outFile, z)
if err != nil {
return err
}
fmt.Println("Finished:", time.Since(timeStart).String())
return nil
}
E finalmente, a medida de desempenho:
helio@goosfraba ~/t/godownloader> go build -o go-downloader-4 main-4.go; time ./go-downloader-4
Downloading: http://replay272.valve.net/730/003705744548740202576_0842061407.dem.bz2
Finished: 3.74774932s
Unpacking: go-003705744548740202576_0842061407.dem
Finished: 6.108665607s
________________________________________________________
Executed in 9.89 secs fish external
usr time 44.22 secs 0.00 micros 44.22 secs
sys time 0.78 secs 655.00 micros 0.78 secs
Vitória! O pbzip2 que faz a descompressão em blocos em paralelo levou 6s. Isso sim é performance. O "usr time" mostra 44s por algum motivo bizarro, mas o tempo total foi por volta de 10s. E o resultado?
helio@goosfraba ~/t/godownloader> sha256sum curl-003705744548740202576_0842061407.dem \
go-003705744548740202576_0842061407.dem
eca9bdd943521251b8704397e40b7f9aada539698561a6c1aca58ebf2602bfc1 curl-003705744548740202576_0842061407.dem
eca9bdd943521251b8704397e40b7f9aada539698561a6c1aca58ebf2602bfc1 go-003705744548740202576_0842061407.dem
Então foi baixado e descompactado bem mais rápido e sem corromper os dados.
Um ponto a ser visto é que talvez exista também um binário pronto com pbzip2 pra descompactar. E pode ser que seja mais rápido que em Go! Mas é pra isso que servem os desafios. Por enquanto vou celebrar minha pequena vitória com uma cerveja.
Quando comecei a aprender Linux e Unix, no século passado, esses dois livros eram meus referenciais sobre ferramentas pra usar. Unix Power Tools e Unix System Administration Handbook. Foram e continuam excelentes livros, mas as ferramentas passaram a ser muito melhores. A grande maioria re-escrita em rust ou em Go! pra maior velocidade e até mesmo segurança.
Eu usava uma ferramenta aqui e outra ali até ler um artigo sobre o assunto e depois achar uma boa referência no GitHub. O artigo que li no começo eu já não lembro e não tenho o link, mas as ferramentas eu fui pegando por essa referência aqui:
https://github.com/ibraheemdev/modern-unix
Então vou listar as ferramentas que uso atualmente. Pegue a pipoca e sente bem confortavelmente que são várias. Não existe ordem de preferência e eu geralmente não adoto duas ferramentas pra mesma coisa. Mostro aqui o que é de uso no dia à dia de trabalho.
Eu estou colocando o link pra cada uma na imagem, mas a grande maioria funciona pelo sistema de pacotes das distros. Dê uma olhada nisso antes de baixar do site.
Eu sinta uma pena muito grande de ver quem fica preso ao bash nos dias de hoje. Não me entenda mal se você é uma dessas pessoas: bash é excelente. Mas existem alternativas muito superiores. Uma delas é o fish. Eu ouvi falar pela primeira vez do fish através do Kov na mesa do Debian numa das edições do FISL. E confesso que achei... aquilo não era pra mim. Eu estava confortável no meu bash. Eu não poderia estar mais enganado.
Na segunda tentativa, eu me forcei a usar o fish por no mínimo 6 meses. E o que aconteceu? Não consigo mais ficar sem ele. É o meu shell de linha de comando em todo lugar. Eu ainda uso o bash pra scripts, mas pra linha de comando, fish shell.
Esse eu conheci mais recentemente por uma dica do Mastodon. Eu sinceramente não lembro quem recomendou, mas lembro de bastante gente elogiando. Testei e gostei. É um formatador de prompt. Ao invés de ficar configurando temas no shell, é só botar o starship pra carregar no login e tá pronto.
O bat é um substituto bem legal ao cat. É mais lento que o cat, mas traz ferramentas visuais com cores que facilitam olhar um arquivo. Isso te faz achar que a pequena lentidão pra ler o resultado vale o esforço, ou tempo.
Nota: por alguma motivo bizarro, no Ubuntu o binário instalado é batcat. Eu geralmente faço um link simbólico pra apenas bat.
Esse é um excelente substituto ao "top". Bem mais gráfico e colorido. Eu nem considero algo tão moderno assim porque uso já faz mais de 10 anos. Mas recentemente descobri que tem gente que não sabe de sua existência. Então fica aqui na lista registrado.
Um programa que subtitui o "df" pra ver o tamanho e uso de partições.
Usa o "du" pra ver qual o tamanho de arquivos e/o ou diretórios? Pois chegou sua evolução.
Esses são substitutos ao "ls". A sintaxe não é 100% compatível. Por exemplo "-tr" não funciona neles, mas você pode usar algo mais simples como "--sort=created". O programa começou como exa, que tem disponível no Ubuntu 24.04, mas no Archlinux passaram pra eza. Imagino que foi um fork porque o primeiro deve ter parado o desenvolvimento.
Ainda usa "ps" pra listar processos? E fica bolado que quando manda um "grep", o mesmo aparece na listagem? Pois o procs resolve esse problema.
O ripgrep, que apesar do nome tem como binário apenas "rg", é um excelente substituto do grep pra procurar em arquivos de determinado diretório. Ou diretórios. E bem mais rápido.
O glances é escrito em python. Legal, mas isso o torna lento pra iniciar. Mas depois que inicia, funciona bem. Ele é um melhoramento do "htop" com mais informação.
Se você mexe com kubernetes ou com arquivos json, jq é a ferramenta. Ajuda a filtrar e dá o resultado num formato colorido, o que ajuda a visualizar. E você pode construir filtros pros resultados. Existe uma outra ferramenta que é mais rápida, mas eu acabo usando o jq por causa da saída colorida (que ajuda demais).
Sabe aquele problema de ler manpage e não ter exemplos? O tldr resolve essa parte. Baixa exemplos do comando que quiser usar.
No Ubuntu o pacote chama-se fd-find. E instala o binário como fdfind. Eu renomeio pra fd, tal qual é no archlinux e no macOS. É um melhoramento do find. Mais rápido e mais fácil de usar.
Esse entra aqui como uma menção quase que honrosa porque atualmente eu já não uso mais. Ele é prático se você usar outro shell que não seja o fish, como bash ou zsh. Ele faz a busca no histórico ser de uma forma bem mais simples. Tal como já é por padrão no fish.
A imagem é do oh-my-zsh, que deixa o terminal mais bonitinho. O zsh mesmo não tem imagem na página do projeto. Se é que é um projeto.
O zsh é o padrão no macOS e eu resolvi experimentar por 6 meses na máquina que peguei no refresh da Ericsson. A vantagem do zsh é que é a mesma sintaxe do bash, então você reaproveita muita coisa. Agora sobre trocar zsh por bash... no final eu achei bem parecido. Existem algumas vantagens do zsh, mas no uso do dia à dia, pra mim pelo menos, não fez tanta diferença assim.
O Patola lembrou bem que existem comportamentos distintos entre bash e zsh.
Em bash:
patola@risadinha:/dados$ echo a | read b ; echo $b
patola@risadinha:/dados$
Em zsh:
[19:03] [9665] [patola@risadinha /dados]% echo a | read b ; echo $b
a
[19:03] [9666] [patola@risadinha /dados]%
O resultado é diferente porque a sintaxe é diferente. O bash tem right-side fork, o zsh tem left-side fork. Isso não é controverso. É uma diferença de sintaxe conhecida, explicada e verificável.
-- Patola
Essa não é bem uma dica pra quem mexe com kubernetes. Ao invés de ficar mandando "kubectl" pra tudo quanto é lado, o melhor é usar o k9s. Ele mostra de forma visual o seu cluster e namespace. E todos os atributos que consegue ver pelo "kubectl".
E assim terminamos o artigo. Se eu lembrar de mais alguma coisa que uso e esqueci, acrescento aqui.
Em minha empolgação com o brinquedo novo, o Lenovo Thinkpad T480, eu simplesmente não percebi que várias teclas são adesivos colados.
Fui mirim.
Pode não parecer, mas mudamos. Já estamos na VPS nova, que fica na Hetzner.
Está no datacenter que fica na Finlândia. Então é quase vizinho daqui.
Espero não levar nenhum susto no fim do mês ao pagar o uso. Por enquanto parece tudo ok.
Sem mais, seguimos em frente.
Estávamos discutindo sobre upgrade de firmware no grupo Linux Brasil eu resolvi dar uma revisitada no artigo que escrevi sobre vulnerabilidades de CPU: falhas de segurança em CPU nas distros com linux-libre.
Olhando aqui meu desktop, estou até que bem protegido.
root@goosfraba ~# for v in /sys/devices/system/cpu/vulnerabilities/*
echo -n "$v:"; cat $v
end
/sys/devices/system/cpu/vulnerabilities/gather_data_sampling:Not affected
/sys/devices/system/cpu/vulnerabilities/itlb_multihit:Not affected
/sys/devices/system/cpu/vulnerabilities/l1tf:Not affected
/sys/devices/system/cpu/vulnerabilities/mds:Not affected
/sys/devices/system/cpu/vulnerabilities/meltdown:Not affected
/sys/devices/system/cpu/vulnerabilities/mmio_stale_data:Not affected
/sys/devices/system/cpu/vulnerabilities/reg_file_data_sampling:Not affected
/sys/devices/system/cpu/vulnerabilities/retbleed:Mitigation: untrained return thunk; SMT disabled
/sys/devices/system/cpu/vulnerabilities/spec_rstack_overflow:Not affected
/sys/devices/system/cpu/vulnerabilities/spec_store_bypass:Mitigation: Speculative Store Bypass disabled via prctl
/sys/devices/system/cpu/vulnerabilities/spectre_v1:Mitigation: usercopy/swapgs barriers and __user pointer sanitization
/sys/devices/system/cpu/vulnerabilities/spectre_v2:Mitigation: Retpolines; STIBP: disabled; RSB filling; PBRSB-eIBRS: Not affected; BHI: Not affected
/sys/devices/system/cpu/vulnerabilities/srbds:Not affected
/sys/devices/system/cpu/vulnerabilities/tsx_async_abort:Not affected
Eu fiz recentemente um upgrade de firmware aqui usando o fwupgrmgr. Não salvei o resultando, mas salvei o que fiz no sh1bb0l33t.
E falando sh1bb0l33t:
root@sh1bb0l33t ~# for v in /sys/devices/system/cpu/vulnerabilities/*
echo -n "$v:"; cat $v
end
/sys/devices/system/cpu/vulnerabilities/gather_data_sampling:Mitigation: Microcode
/sys/devices/system/cpu/vulnerabilities/itlb_multihit:KVM: Mitigation: VMX disabled
/sys/devices/system/cpu/vulnerabilities/l1tf:Mitigation: PTE Inversion; VMX: conditional cache flushes, SMT vulnerable
/sys/devices/system/cpu/vulnerabilities/mds:Mitigation: Clear CPU buffers; SMT vulnerable
/sys/devices/system/cpu/vulnerabilities/meltdown:Mitigation: PTI
/sys/devices/system/cpu/vulnerabilities/mmio_stale_data:Mitigation: Clear CPU buffers; SMT vulnerable
/sys/devices/system/cpu/vulnerabilities/reg_file_data_sampling:Not affected
/sys/devices/system/cpu/vulnerabilities/retbleed:Mitigation: IBRS
/sys/devices/system/cpu/vulnerabilities/spec_rstack_overflow:Not affected
/sys/devices/system/cpu/vulnerabilities/spec_store_bypass:Mitigation: Speculative Store Bypass disabled via prctl
/sys/devices/system/cpu/vulnerabilities/spectre_v1:Mitigation: usercopy/swapgs barriers and __user pointer sanitization
/sys/devices/system/cpu/vulnerabilities/spectre_v2:Mitigation: IBRS; IBPB: conditional; STIBP: conditional; RSB filling; PBRSB-eIBRS: Not affected; BHI: Not affected
/sys/devices/system/cpu/vulnerabilities/srbds:Mitigation: Microcode
/sys/devices/system/cpu/vulnerabilities/tsx_async_abort:Mitigation: TSX disabled
Se estranharem a sintaxe do comando no shell, é porque estou usando fish pra shell interativo. Adotei e não largo mais.
Voltando ao assunto de updates de firmwares, fwupdmgr faz tudo pra você hoje em dia. Os tempos de ter um disco com Windows ou Dos ou FreeDOS pra atualizar são coisa do passado.
Existe a possibilidade de dar algo errado? Claro. Sempre. Estamos falando de firmware de placa-mãe e HDDs/SSDs. Se o processo parar no meio pode dar errado. E alguns updates acabam provocando efeitos indesejados como lentidão (exemplo de correções pra ataques de side channel de processador). Então algumas pessoas preferem escolher mais meticulosamente os updates antes de aplicar.
Mas se esse não é seu caso, eu fortemente recomendo usar o fwupdmgr e atualizar os firmwares de seu computador.
E deixo aqui a frase dita e escrita pelo grande Kevlin Henney durante uma palestra em Estocolmo na Ericsson, e registrada muito péssimamente pela minha câmera do celular (Kevlin, se estiver lendo isso aqui, eu peço desculpas pela fotografia tão borrada).
Eu resolvi melhorar a previsão e o modelo pra tal sobre o Linux no desktop, uma realidade inegável. Então escrevi algo em python pra fazer isso pra mim o trabalho e usar de algum modelo já pronto.
(venv) helio@goosfraba ~/D/linux-desktop-dominance-forecast (main)> ./forecasting.py
Windows OS X Unknown Linux Chrome OS iOS Android \
Date
2009-01-01 95.42 3.68 0.17 0.64 0.0 0.0 0.0
2009-02-01 95.39 3.76 0.14 0.62 0.0 0.0 0.0
2009-03-01 95.22 3.87 0.16 0.65 0.0 0.0 0.0
2009-04-01 95.13 3.92 0.17 0.66 0.0 0.0 0.0
2009-05-01 95.25 3.75 0.24 0.65 0.0 0.0 0.0
Playstation Other
Date
2009-01-01 0.08 0.02
2009-02-01 0.07 0.02
2009-03-01 0.08 0.02
2009-04-01 0.10 0.02
2009-05-01 0.09 0.02
Windows OS X Unknown Linux Chrome OS iOS Android \
Date
2024-01-01 73.00 16.11 5.33 3.77 1.78 0.0 0.0
2024-02-01 72.17 15.42 6.10 4.03 2.27 0.0 0.0
2024-03-01 72.47 14.68 6.52 4.05 2.27 0.0 0.0
2024-04-01 73.50 14.70 5.34 3.88 2.56 0.0 0.0
2024-05-01 73.91 14.90 4.87 3.77 2.54 0.0 0.0
2024-06-01 72.81 14.97 6.23 4.05 1.93 0.0 0.0
2024-07-01 72.10 14.92 7.13 4.44 1.41 0.0 0.0
Playstation Other
Date
2024-01-01 0.0 0.01
2024-02-01 0.0 0.01
2024-03-01 0.0 0.01
2024-04-01 0.0 0.01
2024-05-01 0.0 0.01
2024-06-01 0.0 0.01
2024-07-01 0.0 0.01
/home/helio/DEVEL/linux-desktop-dominance-forecast/venv/lib/python3.12/site-packages/statsmodels/tsa/base/tsa_model.py:473: ValueWarning: No frequency information was provided, so inferred frequency MS will be used.
self._init_dates(dates, freq)
/home/helio/DEVEL/linux-desktop-dominance-forecast/venv/lib/python3.12/site-packages/statsmodels/tsa/base/tsa_model.py:473: ValueWarning: No frequency information was provided, so inferred frequency MS will be used.
self._init_dates(dates, freq)
/home/helio/DEVEL/linux-desktop-dominance-forecast/venv/lib/python3.12/site-packages/statsmodels/tsa/statespace/sarimax.py:978: UserWarning: Non-invertible starting MA parameters found. Using zeros as starting parameters.
warn('Non-invertible starting MA parameters found.'
RUNNING THE L-BFGS-B CODE
* * *
Machine precision = 2.220D-16
N = 11 M = 10
This problem is unconstrained.
At X0 0 variables are exactly at the bounds
At iterate 0 f= 2.00575D-01 |proj g|= 1.24633D+00
At iterate 5 f= -1.49635D-01 |proj g|= 1.77236D-01
At iterate 10 f= -3.64537D-01 |proj g|= 1.24837D-01
At iterate 15 f= -3.84843D-01 |proj g|= 1.22570D-01
At iterate 20 f= -4.20877D-01 |proj g|= 9.71167D-02
At iterate 25 f= -4.29351D-01 |proj g|= 1.13565D-01
At iterate 30 f= -4.33425D-01 |proj g|= 9.06436D-02
At iterate 35 f= -4.34142D-01 |proj g|= 3.98871D-02
At iterate 40 f= -4.36192D-01 |proj g|= 4.26757D-01
At iterate 45 f= -4.37801D-01 |proj g|= 6.82859D-02
At iterate 50 f= -4.37938D-01 |proj g|= 1.67285D-02
* * *
Tit = total number of iterations
Tnf = total number of function evaluations
Tnint = total number of segments explored during Cauchy searches
Skip = number of BFGS updates skipped
Nact = number of active bounds at final generalized Cauchy point
Projg = norm of the final projected gradient
F = final function value
* * *
N Tit Tnf Tnint Skip Nact Projg F
11 50 55 1 0 0 1.673D-02 -4.379D-01
F = -0.43793754789434586
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT
/home/helio/DEVEL/linux-desktop-dominance-forecast/venv/lib/python3.12/site-packages/statsmodels/base/model.py:607:
ConvergenceWarning: Maximum Likelihood optimization failed to converge. Check mle_retvals
warnings.warn("Maximum Likelihood optimization failed to "
The year of Linux on the Desktop: 2036-11-30 00:00:00
Se ignorarmos os pequenos erros e avisos que aparecem, coisa pouca e irrelevante como valor divergir demais, podemos ver claramente quando o ano do Linux no desktop acontece: 2036-11-30
Então a previsão revisada é que logo estaremos em todos os lugares. Aguardem-nos!
E claro que publiquei isso tudo no GitHub:
E usei o seguinte artigo como referência (e código, diga-se de passagem):
Bom ano do Linux no desktop pra todos vocês.
Parece que finalmente acordaram pra aquilo que era óbvio: Linux chegou pra dominar também o desktop!
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed molestie scelerisque ultrices. Nullam venenatis, felis ut accumsan vestibulum, diam leo congue nisl, eget luctus sapien libero eget urna. Duis ac pellentesque nisi.
Foram meros 4%, mas a tendência é de crescimento e não tem volta. Até mesmo peguei os dados diretamente do site statcounter.
Claramente Linux está em plena ascensão. Seja porque razão for, o importante é estar lá. E vai chegar ao 100% com certeza. Eu até fiz uma checagem usando uma função forecast do libreoffice pra ver quantos anos serão necessários.
Ou seja, Linux dominará completamente o desktop em outubro de 2151. Marquem em suas agendas pois será uma data inesquecível. E não temos meta, mas quando chegarmos na meta nós dobraremos a meta. Aguardem-nos!
Antes que perguntem, não, não vou mudar da Suécia. Nem dentro da Suécia. Ao menos por enquanto.
O que aconteceu foi que essa VPS que atualmente tem esse site e o https://linux-br.org está no limite de uso. Ela tem requisitos modestos que nos atenderam bem por muito tempo. Mas agora muita coisa está caindo por OOM (Out Of Memory) e não tem muito o que fazer.
Entrei em contato com a VPS pra ver se era possível um upgrade sem grandes custos, mas eles não foram tão receptivos ao meu pedido quanto eu imaginava.Enquanto isso encontrei uma solução mais viável na Ionos. Por um preço bastante acessível (9 bidens) é possível ter uma VPS bem melhor e na região da união européia.
Posso ainda selecionar a região, que provavelmente será Alemanha, escolher o sistema operacional, que será o mesmo o atual, Debian, e finalmente vem o preço. 4vCPUs, 4 GB de RAM e 160 GB de disco por 9 bidens mensais. É praticamente tudo o dobro que a atual VPS com exceção do disco. O atual são somente 30 GB. Então será mais que o dobro.
A migração será agora no fim do mês. Então pode ser que o site em outros serviços fiquem fora do ar por algum tempo. Mas voltará. E melhor. Espero. Se não ficar melhor, mudamos novamente. Capitalismo 101.
Page 1 of 33