<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>bisko.be - Dev</title><description>Tecnologia, desenvolvimento, hardware e experimentos.</description><link>https://bisko.be/pt/posts/dev/</link><language>pt</language><item><title>Rodando o Ollama com GPUs antigas no CachyOS</title><link>https://bisko.be/pt/posts/dev/running-ollama-with-older-gpus-on-cachyos/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/running-ollama-with-older-gpus-on-cachyos/</guid><description>Hoje eu estava migrando minha VM com Windows 11 para o Linux, para ter mais flexibilidade nos serviços rodando nela, e encontrei um problema: o Ollama não conseguia rodar nas minhas GPUs antigas - GTX1070 e GTX970</description><pubDate>Sat, 28 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h3 id=&quot;tldr&quot;&gt;TL;DR;&lt;/h3&gt;
&lt;p&gt;O &lt;code&gt;ollama-cuda&lt;/code&gt; no CachyOS vem (neste momento) com suporte apenas para &lt;code&gt;compute &gt;= 7.5&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Se você tem GPUs mais antigas, com &lt;code&gt;compute&lt;/code&gt; anterior, você TEM QUE fazer uma de 2 coisas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Compilar o &lt;code&gt;ollama-cuda&lt;/code&gt; a partir do código-fonte, com suporte aos targets de compute mais antigos&lt;/li&gt;
&lt;li&gt;Instalar o &lt;code&gt;ollama-vulkan&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eu escolhi o &lt;code&gt;ollama-vulkan&lt;/code&gt; e está funcionando perfeitamente!&lt;/p&gt;
&lt;h3 id=&quot;hora-da-história&quot;&gt;Hora da história&lt;/h3&gt;
&lt;p&gt;Hoje tive a inspiração de finalmente desligar minha VM com Windows 11 e migrá-la para o Linux.&lt;/p&gt;
&lt;p&gt;Os requisitos eram conseguir rodar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ollama - para a mágica dos modelos locais&lt;/li&gt;
&lt;li&gt;Docker - para desenvolvimento remoto&lt;/li&gt;
&lt;li&gt;Steam - para jogar&lt;/li&gt;
&lt;li&gt;Jellyfin - para o arquivo de vídeos caseiros&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Agora vou focar apenas no Ollama, porque foi uma experiência interessante e me fez pensar por que as coisas não são mais óbvias.&lt;/p&gt;
&lt;p&gt;A máquina tem 2 GPUs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;GTX 1070 - 8GB&lt;/li&gt;
&lt;li&gt;GTX 970 - 4GB&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Eu queria rodar o Ollama nelas, para brincar com modelos locais e ver como poderia integrá-los em alguns projetos. Por exemplo: anonimizar dados localmente, fazer uma implementação não destrutiva do OpenClaw que sirva para os meus casos de uso, OCR local para documentos que escaneio com o celular, automatizar algumas coisas do gerenciamento cotidiano de uma casa.&lt;/p&gt;
&lt;p&gt;Então, naturalmente, instalei o &lt;code&gt;ollama&lt;/code&gt; e o &lt;code&gt;ollama-cuda&lt;/code&gt;, já que tenho 2 GPUs Nvidia aqui, esperando que funcionasse sem problemas, já que estava funcionando no Windows poucos minutos antes.&lt;/p&gt;
&lt;p&gt;Iniciei o Ollama, carreguei o &lt;code&gt;qwen3:8b&lt;/code&gt; para um experimento rápido e… estava rodando na CPU 🤔&lt;/p&gt;
&lt;p&gt;As GPUs eram perfeitamente capazes de manter o modelo na VRAM, mas por algum motivo o Ollama escolhia rodá-lo na CPU.&lt;/p&gt;
&lt;p&gt;Depois de tentar descobrir o motivo pelos logs, pedi ajuda ao Claude, que chegou a uma conclusão bem desanimadora:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; logs&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; tell&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; story&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; clearly.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Both&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; your&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GPUs&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; are&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; detected&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; but&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; then&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; filtered&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; out&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; because&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; they&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; fail&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CUDA&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; initialization:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  &quot;filtering device which didn&apos;t fully initialize&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; id=GPU-eb132a13-b9f4-40e3-d85d-60f211dfd21e&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; library=CUDA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  &quot;filtering device which didn&apos;t fully initialize&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; id=GPU-550263c8-37e2-2a25-c0b6-10cc7511ed20&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ...&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; library=CUDA&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; reason&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CUDA&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; library&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bundled&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Ollama&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0.17.4&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; was&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; compiled&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; these&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; architectures&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; only:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  CUDA.0.ARCHS&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;=750,800,860,870,880,890,900,1000,1030,1100,1200,1210&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  The&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; minimum&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; supported&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; compute&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; capability&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 7.5&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (Turing &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; RTX&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; 20xx&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; series&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;). Your GPUs are older than that:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  ┌──────────┬────────────────────┬──────────────┐&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;   GPU&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;    │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Compute&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Capability&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Architecture&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  ├──────────┼────────────────────┼──────────────┤&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GTX&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1070&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; │&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 6.1&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Pascal&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;       │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  ├──────────┼────────────────────┼──────────────┤&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GTX&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 970&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;  │&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 5.2&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;                │&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Maxwell&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;      │&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  └──────────┴────────────────────┴──────────────┘&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  Neither&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; is&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; in&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; the&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; supported&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; list,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; so&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CUDA&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; initialization&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; fails&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; both,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; and&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Ollama&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; falls&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; back&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CPU-only&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; inference.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  Your&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; options:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  1.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Downgrade&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Ollama&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; older&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; version&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; that&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; still&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; included&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Pascal/Maxwell&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; support&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (pre-0.6.x &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;era&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; had&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; broader&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; arch&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; coverage,&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; but&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; check&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; release&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; notes&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; for&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; specifics&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  2.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Build&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Ollama&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; source&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; with&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; CUDA_ARCHITECTURES=&quot;52;61;75;80;86&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; include&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; your&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GPU&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; architectures.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  3.&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Upgrade&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; GPUs&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; to&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; at&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; least&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; an&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; RTX&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 2060&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; or&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; newer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (compute &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;7.5+&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;).&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso foi meio decepcionante para mim, porque quero usar as GPUs o máximo possível antes de partir para uma nova.&lt;/p&gt;
&lt;p&gt;Eu não queria realmente recompilar o Ollama a partir do código-fonte, já que ele deveria funcionar de cara, e fiquei muito confuso sobre por que não funcionava, sendo que na instalação do Windows ele suportava essas arquiteturas.&lt;/p&gt;
&lt;p&gt;Essas placas também apareciam como suportadas no site do Ollama!&lt;/p&gt;
&lt;p&gt;Para resumir uma longa história de pesquisas no Google:&lt;/p&gt;
&lt;p&gt;Acontece que o &lt;code&gt;ollama-cuda&lt;/code&gt; vem apenas com os requisitos listados acima… e a sugestão foi instalar o &lt;code&gt;ollama-vulkan&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Depois de fazer isso, tudo passou a funcionar muito bem!&lt;/p&gt;</content:encoded><category>linux</category><category>llm</category><category>ollama</category><category>cachyos</category><category>vulcan</category><category>cuda</category></item><item><title>[Linux] Recuperando uma sessão desconectada do `apt dist-upgrade`</title><link>https://bisko.be/pt/posts/dev/linux-recovering-a-disconnected-apt-dist-upgrade-session/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/linux-recovering-a-disconnected-apt-dist-upgrade-session/</guid><description>Hoje eu estava atualizando minha instalação do Proxmox remotamente pelo console embutido, mas esqueci o que estava fazendo e fechei a aba no meio do processo. O resultado foi que não consegui me conectar de novo, já que os pacotes continuavam sendo atualizados em segundo plano e a atualização tinha feito uma pergunta sobre substituir um arquivo e estava esperando uma resposta</description><pubDate>Wed, 23 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hoje eu estava atualizando minha instalação do Proxmox remotamente pelo console embutido, mas esqueci o que estava fazendo e fechei a aba no meio do processo.&lt;/p&gt;
&lt;p&gt;O resultado foi que não consegui me conectar de novo, já que os pacotes continuavam sendo atualizados em segundo plano e a atualização tinha feito uma pergunta sobre substituir um arquivo e estava esperando pela minha resposta.&lt;/p&gt;
&lt;p&gt;Ou seja, eu conseguia entrar via SSH, mas não podia fazer nada, porque o processo ainda esperava uma resposta num console ao qual eu não conseguia me conectar, já que a interface não abria.&lt;/p&gt;
&lt;p&gt;Depois de uma olhada rápida por aí, tropecei em uma &lt;a href=&quot;https://serverfault.com/questions/19634/how-to-reconnect-to-a-disconnected-ssh-session&quot;&gt;thread do Serverfault&lt;/a&gt; que mencionava o uso do &lt;code&gt;reptyr&lt;/code&gt; para assumir o controle da sessão de terminal via SSH.&lt;/p&gt;
&lt;p&gt;Estou registrando aqui exatamente o que foi feito para recuperar a sessão e terminar a atualização:&lt;/p&gt;
&lt;p&gt;A partir &lt;a href=&quot;https://serverfault.com/questions/19634/how-to-reconnect-to-a-disconnected-ssh-session#comment1506308_979180&quot;&gt;deste comentário&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;git&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; clone&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://github.com/nelhage/reptyr&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; reptyr&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;make&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso instalou o &lt;code&gt;reptyr&lt;/code&gt; na máquina.&lt;/p&gt;
&lt;p&gt;Depois procurei todas as sessões abertas com &lt;code&gt;ps ax | grep pts&lt;/code&gt; para encontrar o processo que eu queria.&lt;/p&gt;
&lt;p&gt;Então encontrei o processo mais acima na árvore de chamadas que tinha um &lt;code&gt;pts&lt;/code&gt; associado a ele e me anexei a ele pelo seu PID:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;reptyr&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -T&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1390511&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Aí eu consegui terminar a atualização! 🎉&lt;/p&gt;</content:encoded><category>Uncategorized</category></item><item><title>[Linux] Syncthing – sistema de arquivos somente leitura ao rodar como serviço</title><link>https://bisko.be/pt/posts/dev/linux-syncthing-read-only-file-system-when-running-as-service/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/linux-syncthing-read-only-file-system-when-running-as-service/</guid><description>Já faz alguns meses que uso o Syncthing para sincronizar minha cópia de trabalho com um servidor de desenvolvimento local. No começo, eu o usava logando e rodando manualmente pela CLI. Estava funcionando bem, então nem pensei duas vezes quando passei a rodá-lo como serviço.</description><pubDate>Wed, 06 Apr 2022 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Já faz alguns meses que uso o &lt;a href=&quot;https://syncthing.net/&quot;&gt;Syncthing&lt;/a&gt; para sincronizar minha cópia de trabalho com um servidor de desenvolvimento local.&lt;/p&gt;
&lt;p&gt;No começo, eu o usava logando e rodando manualmente pela CLI. Estava funcionando bem, então nem pensei duas vezes quando passei a rodá-lo como serviço. Ele iniciou, sincronizou alguns dados, tudo certo.&lt;/p&gt;
&lt;p&gt;Até que não estava, claro! 🙂&lt;/p&gt;
&lt;p&gt;Acontece que eu não percebi um monte de erros aparecendo nos logs dizendo que o sistema de arquivos no servidor de desenvolvimento era somente leitura, e isso estava fazendo o Syncthing não sincronizar nada.&lt;/p&gt;
&lt;p&gt;Achei que fosse um bug e que ele só estava mostrando &lt;code&gt;Out of Sync&lt;/code&gt; para as pastas, mas que na verdade estava sincronizando. Pois é, eu estava errado.&lt;/p&gt;
&lt;p&gt;Depois de algumas horas depurando e sem encontrar nenhuma pista nos fóruns em algumas buscas no Google, cheguei a um &lt;a href=&quot;https://wiki.archlinux.org/title/syncthing#read-only_file_system_error_on_/etc_although_run_as_root&quot;&gt;artigo sobre o Syncthing na ArchWiki&lt;/a&gt;, que mencionava exatamente o meu caso:&lt;/p&gt;
&lt;p&gt;read-only file system error on /etc although run as root&lt;/p&gt;
&lt;p&gt;Era exatamente o meu caso de uso, exceto que a pasta problemática não era &lt;code&gt;/etc&lt;/code&gt;, e sim &lt;code&gt;/usr/local/src&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Bem, depois de ler o que dizia, descobri que, se você roda o Syncthing (ou qualquer coisa) via &lt;code&gt;systemd&lt;/code&gt; e tem &lt;code&gt;ProtectSystem&lt;/code&gt; configurado como &lt;code&gt;full&lt;/code&gt; para o serviço, alguns diretórios ficam somente leitura por padrão. &lt;code&gt;/usr&lt;/code&gt; é um desses caminhos.&lt;/p&gt;
&lt;p&gt;Para resolver, você precisa adicionar uma nova linha na configuração do serviço e reiniciar o serviço (e rodar &lt;code&gt;daemon-reload&lt;/code&gt;):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ReadWritePaths=/usr/local/src&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Depois disso, o Syncthing deve funcionar normalmente.&lt;/p&gt;</content:encoded><category>Homelab</category><category>linux</category><category>permissions</category><category>read-only</category><category>syncthing</category><category>systemd</category></item><item><title>[Linux] Executando vários comandos em vários servidores em paralelo</title><link>https://bisko.be/pt/posts/dev/linux-running-multiple-commands-on-multiple-servers-in-parallel/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/linux-running-multiple-commands-on-multiple-servers-in-parallel/</guid><description>Tenho um cluster de Raspberry Pis em casa rodando Docker Swarm. Para manter tudo em ordem, todos usam a mesma configuração – usuários, versões do Docker, etc. Às vezes, algumas atualizações exigem rodar a mesma coisa em todos os nós do cluster – por exemplo – criar uma nova rede macvlan do Docker. Para</description><pubDate>Mon, 19 Apr 2021 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Tenho um cluster de Raspberry Pis em casa rodando Docker Swarm. Para manter tudo em ordem, todos usam a mesma configuração – usuários, versões do Docker, etc.&lt;/p&gt;
&lt;p&gt;Às vezes, algumas atualizações exigem rodar a mesma coisa em todos os nós do cluster – por exemplo – criar uma nova rede macvlan do Docker.&lt;/p&gt;
&lt;p&gt;Para fazer isso, é preciso rodar os mesmos comandos em todos os nós manualmente, entrando via &lt;strong&gt;ssh&lt;/strong&gt; em cada um deles, executando os comandos e passando para o próximo.&lt;/p&gt;
&lt;p&gt;Como estou tentando reduzir meu trabalho a longo prazo e deixar tudo o mais organizado possível, comecei a procurar maneiras de rodar comandos em todos os nós ao mesmo tempo.&lt;/p&gt;
&lt;h3 id=&quot;iteração-1-script-python-customizado&quot;&gt;Iteração 1: Script Python customizado&lt;/h3&gt;
&lt;p&gt;A primeira iteração foi criar um script Python customizado. Por um lado, era para aprender um pouco melhor Python; por outro, eu não tinha encontrado uma forma boa de rodar os comandos de um jeito mais nativo.&lt;/p&gt;
&lt;p&gt;Como era de se esperar (ei, ainda tenho pouca experiência com a linguagem 🙂 ), ele virou uma bagunça inchada muito rápido e quebrava com facilidade.&lt;/p&gt;
&lt;p&gt;Eu precisava achar outro jeito de fazer isso, sem ter que reaprender o que eu fiz 3 semanas atrás e o que quebrou da última vez.&lt;/p&gt;
&lt;h3 id=&quot;iteração-2-pssh&quot;&gt;Iteração 2: &lt;code&gt;pssh&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Pesquisando por aí, esbarrei no &lt;code&gt;[pssh](https://linux.die.net/man/1/pssh)&lt;/code&gt;, que consegue rodar coisas em paralelo via SSH.&lt;/p&gt;
&lt;p&gt;Considerando tudo, foi bem fácil colocá-lo para funcionar.&lt;/p&gt;
&lt;p&gt;O problema era que, para rodar algumas das coisas, eu precisava executá-las via &lt;code&gt;sudo&lt;/code&gt;. Ficar passando senhas por aí não era o ideal, então tive que pesquisar de novo para descobrir como enviar a senha do &lt;code&gt;sudo&lt;/code&gt; quando o servidor remoto a pedisse.&lt;/p&gt;
&lt;p&gt;Acontece que é bem fácil se você topar com o &lt;a href=&quot;https://unix.stackexchange.com/a/487558/134974&quot;&gt;comentário certo no StackOverflow&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;a-versão-resumida-tldr-é&quot;&gt;A versão resumida (TL;DR;) é:&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stty&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -echo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;printf&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Password: &quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;read&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; PASS&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;stty&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; echo&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;${&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;PASS&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;}&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; |&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;pssh&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -h&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;HOSTFIL&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;E&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -o&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; /tmp/output&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -t&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -I&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;`&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;cat&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ./commands.sh`&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;O &lt;code&gt;-I &quot;`cat ./commands.sh`&quot;&lt;/code&gt; precisa ser feito exatamente assim (entre aspas e crases desse jeito); caso contrário, ele não executa os comandos direito. A senha vai ser enviada como um comando separado, e não captada como entrada do &lt;strong&gt;sudo&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A outra pegadinha é que, por causa de como isso está montado, é preciso rodar o &lt;code&gt;sudo&lt;/code&gt; com o parâmetro &lt;code&gt;-S&lt;/code&gt;, para que ele leia a senha corretamente.&lt;/p&gt;</content:encoded><category>Uncategorized</category></item><item><title>Descobrindo a senha numérica de um arquivo zip usando apenas utilitários de linha de comando</title><link>https://bisko.be/pt/posts/dev/guessing-a-numeric-zip-file-password-only-with-command-line-utilities/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/guessing-a-numeric-zip-file-password-only-with-command-line-utilities/</guid><description>Hoje recebi minha fatura mensal do meu provedor de internet e algo me pareceu estranho. A fatura, contida no e-mail, estava em um arquivo ZIP com senha. Sem falar o quão estranho foi receber um anexo em formato ZIP nos dias de hoje (spam, malware, phishing, etc.), a</description><pubDate>Mon, 14 Sep 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hoje recebi minha fatura mensal do meu provedor de internet e algo me pareceu estranho.&lt;/p&gt;
&lt;p&gt;A fatura, contida no e-mail, estava em um arquivo ZIP com senha.&lt;/p&gt;
&lt;p&gt;Sem falar o quão estranho foi receber um anexo em formato ZIP nos dias de hoje (spam, malware, phishing, etc.), a senha que escolheram me pareceu muito fraca.&lt;/p&gt;
&lt;p&gt;É a data de nascimento do titular da conta no formato &lt;code&gt;YYMMDD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Fiz um cálculo bem grosseiro de cabeça enquanto estava na academia e deu um número bem pequeno de combinações possíveis. Depois de usar uma ferramenta online para calcular quantos dias se passaram (já que nem todas as combinações resultariam em uma data válida) desde 1900-01-01, descobri que as combinações são apenas 44.075 (até 2020-09-14).&lt;/p&gt;
&lt;p&gt;Sabendo que ferramentas otimizadas que usam o poder da GPU para quebrar hashes conseguem fazer tentativas na casa dos milhões por segundo, eu quis ver quão rápido a senha poderia ser descoberta sem usá-las, usando apenas a CLI do Linux/macOS (já que só tinha acesso a uma VPS enquanto estava na academia).&lt;/p&gt;
&lt;p&gt;Cheguei ao seguinte código, que funciona no macOS:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;echo {1..44000} | xargs -P 16 -n 1 -I{} date -v &quot;-{}d&quot; +&quot;%y%m%d&quot; | xargs -P 16 -I{} sh -c &apos;unzip -qqoP &quot;{}&quot; 0417160508.zip &amp;#x26;&amp;#x26; echo &quot;{}&quot; &amp;#x26;&amp;#x26; exit 255&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;O comando é bem simples em seu funcionamento, mesmo parecendo complicado.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;echo {1..44000}&lt;/code&gt; gera os números de 1 a 44.000 e os repassa para a próxima etapa.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;xargs -P 16 -n 1 -I{} date -v &quot;-{}d&quot; +&quot;%y%m%d&quot;&lt;/code&gt; – pega a entrada da primeira etapa e gera datas no formato &lt;code&gt;YYMMDD&lt;/code&gt;, aproveitando a funcionalidade do &lt;code&gt;date&lt;/code&gt; de “aumentar” uma data com uma quantidade específica. Nesse caso, seria subtrair X dias da data atual, onde X é o número gerado na primeira etapa. Dessa forma, conseguimos gerar datas de hoje até os anos 1900.&lt;/p&gt;
&lt;p&gt;A terceira etapa é a mais interessante, então vou dividi-la em duas partes:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;xargs -P 16 -I{} sh -c &apos;...&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Isso pega as datas geradas na etapa 2 e executa um comando em cada uma dessas datas. &lt;code&gt;-P 16&lt;/code&gt; aqui significa que isso vai rodar em 16 processos paralelos, para tornar a varredura dos dados mais rápida. O mesmo truque é aplicado na etapa 2 para acelerar a geração das datas.&lt;/p&gt;
&lt;p&gt;A segunda parte é onde as tentativas acontecem:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;unzip -qqoP &quot;{}&quot; 0417160508.zip &amp;#x26;&amp;#x26; echo &quot;{}&quot; &amp;#x26;&amp;#x26; exit 255&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;O que isso faz é primeiro tentar descompactar o arquivo zip usando a senha gerada na etapa 2. Se for bem-sucedido, ele imprime (&lt;code&gt;echo&lt;/code&gt;) a senha descoberta e então &lt;code&gt;exit 255&lt;/code&gt; força o &lt;code&gt;xargs&lt;/code&gt; a parar de rodar. Não queremos continuar tentando senhas se a correta já foi encontrada.&lt;/p&gt;
&lt;p&gt;No fim das contas, foi uma decisão bem ruim escolher isso como senha do arquivo, porque rodar o comando acima leva apenas cerca de 10 segundos para descobrir a senha correta da minha fatura. Não muito mais para qualquer outra pessoa.&lt;/p&gt;</content:encoded><category>Uncategorized</category></item><item><title>Lidando com código espaguete em PHP: usando funções anônimas para extrair pedaços de código de template</title><link>https://bisko.be/pt/posts/dev/dealing-with-spaghetti-code-in-php-using-anonymous-functions-to-extract-pieces-of-template-code/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/dealing-with-spaghetti-code-in-php-using-anonymous-functions-to-extract-pieces-of-template-code/</guid><description>Recentemente tenho ajudado um amigo a entender um código e a descobrir como modificá-lo para funcionar bem com chamadas assíncronas ao backend. Um bônus seria conseguir escrevê-lo de um jeito mais modular e reutilizável. O código é um plugin de WordPress que foi escrito</description><pubDate>Sat, 08 Aug 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recentemente tenho ajudado um amigo a entender um código e a descobrir como modificá-lo para funcionar bem com chamadas assíncronas ao backend. Um bônus seria conseguir escrevê-lo de um jeito mais modular e reutilizável.&lt;/p&gt;
&lt;p&gt;O código é um plugin de WordPress escrito com uma abordagem bem legada: lógica de negócio e de frontend misturadas num lugar só. Manipulação de dados feita no meio do código de template, e coisas do tipo.&lt;/p&gt;
&lt;p&gt;Basicamente, o que você via em sites PHP no começo dos anos 2000, antes de todas as incríveis adições de OOP à linguagem e de um monte de frameworks maduros.&lt;/p&gt;
&lt;p&gt;O caso em que eu estava trabalhando hoje era o seguinte: você tem uma tabela que mostra um monte de registros. Precisávamos adicionar vários filtros à tabela e fazê-la funcionar via AJAX, para atualizar a interface rapidamente, sem recarregar a página inteira do Admin do WordPress.&lt;/p&gt;
&lt;p&gt;Filtrar os dados foi fácil: um &lt;code&gt;array_filter&lt;/code&gt; inline rapidinho para filtrar os dados antes de passá-los ao código do template (para manter o mesmo estilo de código, isso também ficou no arquivo de template 😇).&lt;/p&gt;
&lt;p&gt;O problema apareceu quando começamos a trabalhar para tornar a tabela renderizável via AJAX.&lt;/p&gt;
&lt;p&gt;Ela está no meio de um monte de outros códigos de template e de negócio. Se quiséssemos limitar a saída via AJAX só à tabela, só tínhamos algumas maneiras de fazer isso.&lt;/p&gt;
&lt;p&gt;A primeira seria envolver o HTML antes e depois da tabela em &lt;code&gt;if&lt;/code&gt;s ENORMES para garantir que ele não fosse renderizado.&lt;/p&gt;
&lt;p&gt;Outra seria extrair o template para um arquivo separado e incluí-lo na ação de AJAX que definiríamos no código do plugin.&lt;/p&gt;
&lt;p&gt;Funções anônimas para o resgate!&lt;/p&gt;
&lt;p&gt;Pensando em como lidar com isso da maneira mais simples possível, tentei definir uma função PHP normal que conteria o código da tabela, mas havia um problema.&lt;/p&gt;
&lt;p&gt;O código roda dentro de um método de uma classe. Há referências a &lt;code&gt;$this&lt;/code&gt; no template da tabela. Se não queremos mudar muito o código, teríamos que passar &lt;code&gt;$this&lt;/code&gt; como argumento da função. Pois é, isso não funciona, já que você não pode passar &lt;code&gt;$this&lt;/code&gt; como parâmetro 🤷‍♂️.&lt;/p&gt;
&lt;p&gt;Fiquei meio decepcionado por termos que reescrever um monte de coisas no template, e havia bastantes referências com as quais precisávamos tomar cuidado para que tudo continuasse funcionando.&lt;/p&gt;
&lt;p&gt;Então lembrei que o site não roda em PHP 5.2, mas em PHP 7, e que funções anônimas seriam suportadas.&lt;/p&gt;
&lt;p&gt;E elas têm um recurso bem prático, em que você define quais variáveis do escopo de quem chama serão passadas para a função quando ela for chamada. Por exemplo:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;php&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;$table_function &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; ( $pages, $current_page, $items ) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // ... Table render code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso permite referenciar diretamente &lt;code&gt;$pages&lt;/code&gt; ou &lt;code&gt;$items&lt;/code&gt; no corpo da função.&lt;/p&gt;
&lt;p&gt;O recurso incrível aqui é que você também pode passar &lt;code&gt;$this&lt;/code&gt; como uma variável no escopo da função!&lt;/p&gt;
&lt;p&gt;Agora conseguimos extrair totalmente o código de renderização da tabela para uma função separada, que podemos chamar isoladamente durante a chamada AJAX!&lt;/p&gt;
&lt;p&gt;Claro que é preciso ter muito cuidado com as variáveis usadas dentro do corpo da função, pois elas precisam estar definidas, então atenção a onde a função de AJAX pode ser chamada.&lt;/p&gt;
&lt;p&gt;Para nós, isso significa que vamos adicionar uma pequena ação na classe principal que ainda vai dar &lt;code&gt;require&lt;/code&gt; no arquivo, mas teremos mais controle para desativar todas as outras saídas que aconteceriam.&lt;/p&gt;</content:encoded><category>Snippets</category><category>lambda-functions</category><category>php</category><category>WordPress</category></item><item><title>Arduino/ESP32: Desabilitando o watchdog de tarefas</title><link>https://bisko.be/pt/posts/dev/arduino-esp32-disabling-the-task-watchdog/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/arduino-esp32-disabling-the-task-watchdog/</guid><description>Recentemente andei brincando com a funcionalidade multicore do ESP32 e com a fixação de tarefas em núcleos específicos. Eu estava usando o AutoConnect para dar suporte à conexão WiFi sem guardar credenciais no código e para oferecer atualizações OTA pela interface web. Era para um projeto que ia controlar alguns coolers de CPU de 4 pinos com PWM que ficam no meu armário de servidores, para resfriar o ar lá dentro.</description><pubDate>Wed, 29 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recentemente andei brincando com a funcionalidade multicore do ESP32 e com a fixação de tarefas em núcleos específicos.&lt;/p&gt;
&lt;p&gt;Eu estava usando o &lt;a href=&quot;https://github.com/Hieromon/AutoConnect&quot;&gt;AutoConnect&lt;/a&gt; para dar suporte à conexão WiFi sem guardar credenciais no código e para oferecer atualizações OTA pela interface web.&lt;/p&gt;
&lt;p&gt;Era para um projeto que ia controlar alguns coolers de CPU de 4 pinos com PWM que ficam no meu armário de servidores, para resfriar o ar lá dentro. Para isso, eu queria dividir a lógica em duas tarefas.&lt;/p&gt;
&lt;p&gt;Uma tarefa para cuidar das APIs, leitura de sensores, atualizações, WiFi etc., e outra para cuidar da interrupção e do controle PWM dos ventiladores.&lt;/p&gt;
&lt;p&gt;Tudo certo, as tarefas foram criadas, mas ao rodar o código, se eu colocava o ESP32 no modo em que você precisa se conectar a ele para dizer a qual rede WiFi ele deve se conectar, ele começava a ter um comportamento estranho: resetava o controlador a cada poucos segundos e eu não conseguia fazer a conexão funcionar.&lt;/p&gt;
&lt;p&gt;Foi um momento chato, ter que desmontar o controlador do lugar dele e ligar no USB para gravar um firmware novo.&lt;/p&gt;
&lt;p&gt;Depois de fazer isso, percebi que o erro vinha do watchdog:&lt;/p&gt;
&lt;p&gt;Task watchdog got triggered. The following tasks did not feed the watchdog in time:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;IDLE (CPU 1)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Tasks currently running:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CPU 0: ManageTask&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;CPU 1: ControllerTask&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pesquisando, parece que isso é disparado pelo Task Watchdog do ESP32, que garante que as tarefas tenham a sua vez de executar na CPU.&lt;/p&gt;
&lt;p&gt;E também parecia que o AutoConnect não era muito flexível quando rodava dentro de uma tarefa, em comparação a rodar fora de uma, direto com todo o resto do código e das funcionalidades.&lt;/p&gt;
&lt;p&gt;Fuçando um pouco e tentando coisas diferentes, parece que a forma mais fácil de sair dessa situação é simplesmente desabilitar o watchdog e torcer para que as coisas sejam executadas a tempo 🙂 A tarefa não é crítica, então tudo bem esperar mais tempo para ela terminar o que está fazendo.&lt;/p&gt;
&lt;p&gt;Para desabilitar o watchdog, você precisa fazer duas coisas:&lt;/p&gt;
&lt;p&gt;Primeiro, incluir o header &lt;code&gt;esp_task_wdt.h&lt;/code&gt;. Para isso, adicione &lt;code&gt;#include &amp;#x3C;esp_task_wdt.h&gt;&lt;/code&gt; na sua seção de includes.&lt;/p&gt;
&lt;p&gt;Segundo, fazer o watchdog não resetar o controlador e esperar mais tempo. Para isso, você precisa adicionar &lt;code&gt;esp_task_wdt_init(30, false);&lt;/code&gt; no início do código da sua Task.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;cpp&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; manage_core_task&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;void&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;pv_parameters&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    esp_task_wdt_init&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;30&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;    // .... rest of the code ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso vai definir o timeout do watchdog para 30 &lt;strong&gt;SEGUNDOS&lt;/strong&gt; e desabilitar o reset do controlador caso ele seja disparado.&lt;/p&gt;
&lt;p&gt;Se você quiser que o controlador ainda seja resetado depois que o watchdog disparar, mude o segundo parâmetro da chamada da função de &lt;code&gt;false&lt;/code&gt; para &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;</content:encoded><category>Uncategorized</category><category>arduino</category><category>code</category><category>esp32</category><category>espressif</category><category>multi-core</category><category>multi-threading</category></item><item><title>Configurando uma YubiKey para assinar commits do Git</title><link>https://bisko.be/pt/posts/dev/setting-up-a-yubikey-for-git-commit-signing/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/setting-up-a-yubikey-for-git-commit-signing/</guid><description>Eu venho usando uma YubiKey como chave de autenticação em 2 fatores e para assinar meus commits do Git. Até alguns dias atrás eu usava uma YubiKey 4. Como meu computador atual não tem nenhuma porta USB-A, eu andava com adaptadores de USB-A para USB-C na mochila e usar a chave virou um incômodo para o</description><pubDate>Wed, 08 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Eu venho usando uma YubiKey como chave de &lt;a href=&quot;https://en.wikipedia.org/wiki/Multi-factor_authentication&quot;&gt;autenticação em 2 fatores&lt;/a&gt; e para assinar meus commits do Git. Até alguns dias atrás eu usava uma YubiKey 4. Como meu computador atual não tem nenhuma porta USB-A, faz mais de um ano que eu ando com adaptadores de USB-A para USB-C na mochila, e usar a chave tem sido um incômodo. Por isso decidi que era hora de migrar para a minha YubiKey 5C “nova” e ficar livre de ter que carregar esses adaptadores.&lt;/p&gt;
&lt;p&gt;Na verdade, eu já tinha a chave há um tempo: comprei junto com uma YubiKey 4 de reposição, que precisei por causa do problema do &lt;a href=&quot;https://www.yubico.com/2017/10/infineon-rsa-key-generation-issue/&quot;&gt;TPM da Infineon&lt;/a&gt; identificado por eles. Mas só agora decidi que era hora de migrar para a chave nova.&lt;/p&gt;
&lt;p&gt;O outro motivo para fazer isso agora é que minhas chaves GPG antigas expiraram e eu precisei reemiti-las, então posso aproveitar para usar a chave para assinar meus commits do Git.&lt;/p&gt;
&lt;h3 id=&quot;a-loucura-do-gpg&quot;&gt;A loucura do GPG&lt;/h3&gt;
&lt;p&gt;Se você já lidou com chaves GPG no passado (ou lida atualmente), sabe que pode ser bem difícil colocá-las para funcionar de forma correta e segura se você nunca fez isso antes. Manter as chaves mestras offline, trocar chaves públicas de um lado para o outro, reemitir chaves depois que expiram, possivelmente assiná-las com chaves anteriores para verificá-las, revogar chaves… Não é fácil seguir as boas práticas nem na teoria. Na prática, as coisas ficam extremamente complicadas.&lt;/p&gt;
&lt;p&gt;A maioria dos tutoriais online manda usar a linha de comando para gerar as chaves, navegando por menus com um padrão de design nada comum, e é uma dor de cabeça geral para fazer tudo funcionar. A maioria deles diz para usar uma máquina offline, como um Raspberry PI, e/ou gerar as chaves mestras e subchaves offline e manter a chave mestra fora do seu computador.&lt;/p&gt;
&lt;p&gt;🤯&lt;/p&gt;
&lt;p&gt;Eu lido com isso todo começo de janeiro há quase 4 anos. Eu morro de medo do momento em que aparece o erro a seguir, avisando que minhas chaves de assinatura expiraram.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;error: gpg failed to sign the data&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;fatal: failed to write commit object&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Este ano decidi que era hora de tentar deixar esse processo muito mais fácil, usando a geração de certificados embutida na própria chave e evitando todo o trabalho de gerar chaves GPG e afins.&lt;/p&gt;
&lt;h3 id=&quot;geração-de-chaves-direto-na-yubikey&quot;&gt;Geração de chaves direto na YubiKey&lt;/h3&gt;
&lt;p&gt;Ainda vamos ter que usar a interface de linha de comando, mas é um pouco mais óbvio, já que tem menos etapas.&lt;/p&gt;
&lt;p&gt;Primeiro precisamos configurar nosso cartão/dispositivo.&lt;/p&gt;
&lt;h3 id=&quot;configuração-do-cartãodispositivo&quot;&gt;Configuração do cartão/dispositivo&lt;/h3&gt;
&lt;p&gt;Execute &lt;code&gt;gpg --card-status&lt;/code&gt; no terminal e você deve ver algo assim:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Reader ………..: Yubico Yubikey 4 OTP U2F CCID&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Application ID …: &amp;#x3C;Serial number&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Version ……….: 2.1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Manufacturer …..: Yubico&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Serial number ….: &amp;#x3C;Device serial number&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Name of cardholder: unspecified&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Language prefs …: en&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Sex …………..: unspecified&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;URL of public key : \[not set\]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Login data …….: &amp;#x3C;e-mail or nothing&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Signature PIN ….: not forced&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Key attributes …: rsa4096 rsa4096 rsa4096&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Max. PIN lengths .: 127 127 127&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;PIN retry counter : 3 0 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Signature counter : 9&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se você viu algo parecido e nenhum erro apareceu, pode continuar.&lt;/p&gt;
&lt;p&gt;Execute &lt;code&gt;gpg --card-edit&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Você vai cair em uma interface de linha de comando, mostrando as mesmas informações do início e, embaixo, um prompt de comando &lt;code&gt;gpg/card&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Digite &lt;code&gt;admin&lt;/code&gt; e aperte return/enter.&lt;/p&gt;
&lt;p&gt;Agora você pode digitar &lt;code&gt;help&lt;/code&gt; para descobrir como alterar as informações do dono e os PINs do cartão.&lt;/p&gt;
&lt;p&gt;Uma coisa que você &lt;strong&gt;&lt;em&gt;PRECISA&lt;/em&gt;&lt;/strong&gt; fazer é trocar os PINs padrão do cartão. São dois. O primeiro é o PIN que você usa para acessar as chaves no cartão. O segundo é o PIN de “Admin” ou PUK (Personal Unblocking Code, o código pessoal de desbloqueio).&lt;/p&gt;
&lt;p&gt;Para isso, digite &lt;code&gt;passwd&lt;/code&gt; e você verá um menu:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg/card&gt; passwd  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg: OpenPGP card no. &amp;#x3C;Serial number&gt; detected&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1 - change PIN  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2 - unblock PIN  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3 - change Admin PIN  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4 - set the Reset Code  &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Q - quit&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Digite &lt;code&gt;1&lt;/code&gt; e siga as instruções para atualizar o seu PIN. O PIN padrão é &lt;code&gt;123456&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Depois de terminar, escolha &lt;code&gt;3&lt;/code&gt; para trocar o PIN de Admin. O padrão é &lt;code&gt;12345678&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Com os dois PINs trocados, volte ao menu principal para terminar de atualizar as informações da chave.&lt;/p&gt;
&lt;p&gt;Os comandos que você pode usar são:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;name&lt;/code&gt; – para alterar o nome do titular do cartão&lt;/li&gt;
&lt;li&gt;&lt;code&gt;login&lt;/code&gt; – para alterar o endereço de e-mail ou o login do titular do cartão&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Depois disso, podemos passar para a geração das chaves propriamente dita.&lt;/p&gt;
&lt;h3 id=&quot;geração-das-chaves&quot;&gt;Geração das chaves&lt;/h3&gt;
&lt;p&gt;Agora é hora de gerar as chaves de verdade.&lt;/p&gt;
&lt;p&gt;Para isso, digite &lt;code&gt;generate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Vão te perguntar &lt;code&gt;Make off-card backup of encryption key? (Y/n)&lt;/code&gt;, mas, como diz a documentação da YubiKey, isso é só um backup de fachada e não cria um backup completo das chaves secretas, então tanto faz responder &lt;code&gt;Yes&lt;/code&gt; ou &lt;code&gt;No&lt;/code&gt;. Eu respondo &lt;code&gt;No(n)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Se já houver chaves armazenadas no cartão, vão perguntar se você quer substituí-las:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg: Note: keys are already stored on the card!&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Replace existing keys? (y/N)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Verifique se você não vai substituir algo importante do qual não tem backup e continue com &lt;code&gt;Y&lt;/code&gt;. Se houver algo importante, é melhor cancelar todo o processo com &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Vão pedir o PIN do cartão que você definiu antes.&lt;/p&gt;
&lt;p&gt;Depois você terá que escolher por quanto tempo as chaves serão válidas:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Please specify how long the key should be valid.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;        0 = key does not expire&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;      &amp;#x3C;n&gt; = key expires in n days&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     &amp;#x3C;n&gt;w = key expires in n weeks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     &amp;#x3C;n&gt;m = key expires in n months&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     &amp;#x3C;n&gt;y = key expires in n years&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; Key is valid for? (0)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Se você quer que as chaves sejam válidas por apenas 1 ano, digite &lt;code&gt;1y&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Vão pedir para você confirmar a data de expiração. Confira se está tudo certo e confirme.&lt;/p&gt;
&lt;p&gt;Depois vão pedir as informações do dono da chave:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Is this correct? (y/N) y&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;GnuPG needs to construct a user ID to identify your key.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Real name: &amp;#x3C;Your name&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Email address: &amp;#x3C;Your e-mail&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Comment: &amp;#x3C;Comment is optional&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; You selected this USER-ID:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;     &quot;&amp;#x3C;Your name&gt; &amp;#x3C;comment&gt; &amp;#x3C;your e-mail&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt; Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Type `O` to confirm if everything seems correct.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Depois vão pedir o seu PIN de Admin ou PUK. Ele é necessário porque será feita uma operação de escrita no cartão.&lt;/p&gt;
&lt;p&gt;Então, se tudo estiver certo, a YubiKey vai começar a piscar e continuar assim por um tempo (para mim leva uns cinco minutos).&lt;/p&gt;
&lt;p&gt;Quando terminar, você verá a seguinte mensagem:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg: key &amp;#x3C;Short ID&gt; marked as ultimately trusted&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;gpg: revocation certificate stored as &apos;/Users/bisko/.gnupg/openpgp-revocs.d/&amp;#x3C;LONG ID&gt;.rev&apos; public and secret key created and signed.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anote o &lt;code&gt;&amp;#x3C;Short ID&gt;&lt;/code&gt;, pois vamos usá-lo mais adiante.&lt;/p&gt;
&lt;p&gt;Neste ponto você pode sair do prompt do GPG com &lt;code&gt;quit&lt;/code&gt; ou &lt;code&gt;Ctrl+C&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Agora é hora de salvar a chave pública, para que possamos reutilizar a chave em outro computador.&lt;/p&gt;
&lt;p&gt;Para isso, execute o seguinte comando:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --armor&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.asc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso vai exportar a chave pública para um arquivo chamado &lt;code&gt;&amp;#x3C;Short ID&gt;.asc&lt;/code&gt;. Ele será usado no futuro se você quiser usar a YubiKey em outro computador ou se perder a sua configuração do GPG.&lt;/p&gt;
&lt;p&gt;Para restaurar a chave pública no seu chaveiro, você precisa fazer:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --import&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;.asc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Short&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; public&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Your Name &amp;#x3C;your@email&gt;&quot;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; imported&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; Total&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; number&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; processed:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;gpg:&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; imported:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Uma observação muito importante.&lt;/em&gt;&lt;/strong&gt; Se for uma instalação nova do GPG ou outro computador, o GPG não vai reconhecer as chaves do cartão sozinho. Você precisa executar &lt;code&gt;gpg --card-status&lt;/code&gt; para que ele leia o cartão e identifique as chaves nele.&lt;/p&gt;
&lt;h3 id=&quot;configurando-o-git-para-assinar&quot;&gt;Configurando o Git para assinar&lt;/h3&gt;
&lt;p&gt;Para configurar o Git para assinar seus commits, você precisa adicionar o seguinte ao seu &lt;code&gt;~/.gitconfig&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Na seção &lt;code&gt;[user]&lt;/code&gt;, você precisa adicionar (ou atualizar) a seguinte linha:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;signingkey = 0x&amp;#x3C;Short ID&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Depois, se você quiser assinar todos os seus commits, adicione o seguinte no final do arquivo:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;[gpg]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  program&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; = /usr/local/bin/gpg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;[commit]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  gpgsign&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; = true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Isso fará o Git assinar todos os seus commits.&lt;/p&gt;
&lt;p&gt;Para configurar o GitHub, você precisa adicionar sua chave pública ao seu perfil. Para isso, vá em &lt;code&gt;GitHub -&gt; Settings -&gt; SSH and GPG keys -&gt; New GPG key&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Você verá uma caixa de texto, onde precisa colar todo o conteúdo do arquivo &lt;code&gt;&amp;#x3C;Short ID&gt;.asc&lt;/code&gt; que geramos acima.&lt;/p&gt;
&lt;p&gt;Se quiser copiar o conteúdo do arquivo (no macOS), você pode fazer:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cat &amp;#x3C;Short ID&gt;.asc | pbcopy&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Isso copia o conteúdo do arquivo inteiro para a área de transferência.&lt;/p&gt;
&lt;p&gt;Cole na caixa de texto do GitHub e clique no botão &lt;code&gt;Add GPG key&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Depois disso, você pode tentar fazer um commit e um push para o GitHub e deve ver o lindo selo &lt;code&gt;Verified&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;depuração&quot;&gt;Depuração&lt;/h3&gt;
&lt;p&gt;Às vezes as coisas não funcionam de primeira. Para reunir mais informações, você pode tentar o seguinte:&lt;/p&gt;
&lt;h3 id=&quot;assinando-com-o-gpg&quot;&gt;Assinando com o GPG&lt;/h3&gt;
&lt;p&gt;Primeiro, tente assinar algo com a chave:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;gpg -s &amp;#x3C;file&gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Se isso falhar, tente descobrir por quê. Eu não sou especialista para ajudar nessa parte.&lt;/p&gt;
&lt;p&gt;Confira a saída do Git com GIT_TRACE=1 git commit -am “test”&lt;/p&gt;
&lt;p&gt;Isso mostra exatamente o que o Git faz enquanto tenta realizar o commit e assiná-lo. Vai te dar uma pista de por que a assinatura pode estar falhando.&lt;/p&gt;
&lt;p&gt;Dali você também pode ver que o Git executa o seguinte comando:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;/usr/local/bin/gpg&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --status-fd=2&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -bsau&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;Key&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; I&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;D&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Você pode tentar executá-lo manualmente para ver se ele dá um erro mais útil.&lt;/p&gt;</content:encoded><category>Snippets</category><category>Technology</category><category>git</category><category>github</category><category>gpg</category><category>key</category><category>public-key</category><category>secrets</category><category>sign</category><category>yubikey</category></item><item><title>e1000e  eth0: Detected Hardware Unit Hang</title><link>https://bisko.be/pt/posts/dev/e1000e-device-id-eth0-detected-hardware-unit-hang/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/e1000e-device-id-eth0-detected-hardware-unit-hang/</guid><description>Recentemente, meu servidor doméstico e host de VMs começou, do nada, a perder a conectividade de rede. Por fora parecia que ele ainda estava funcionando, mas eu não conseguia acessá-lo remotamente de nenhuma forma. O adaptador ethernet parecia ligado, segundo o switch, então o problema devia estar em algum lugar do software. Não seria</description><pubDate>Wed, 18 Dec 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recentemente, meu servidor doméstico e host de VMs começou, do nada, a perder a conectividade de rede. Por fora parecia que ele ainda estava funcionando, mas eu não conseguia acessá-lo remotamente de nenhuma forma. O adaptador ethernet parecia ligado, segundo o switch, então o problema devia estar em algum lugar do software.&lt;/p&gt;
&lt;p&gt;Não seria a primeira vez que um driver causaria travamentos na conexão de rede. No passado, já me ferrei com drivers de WiFi bugados em computadores Linux e Windows.&lt;/p&gt;
&lt;p&gt;Depois de fuçar um pouco nos logs do sistema, me deparei com o seguinte:&lt;/p&gt;
&lt;p&gt;vmhost kernel: e1000e 0000:00:1f.6 eth0: Detected Hardware Unit Hang:&lt;br&gt;
vmhost kernel:   TDH                  &amp;#x3C;0&gt;&lt;br&gt;
vmhost kernel:   TDT                  &amp;#x3C;1&gt;&lt;br&gt;
vmhost kernel:   next_to_use          &amp;#x3C;1&gt;&lt;br&gt;
vmhost kernel:   next_to_clean        &amp;#x3C;0&gt;&lt;br&gt;
vmhost kernel: buffer_info[next_to_clean]:&lt;br&gt;
vmhost kernel:   time_stamp           &amp;#x3C;10fbc2f81&gt;&lt;br&gt;
vmhost kernel:   next_to_watch        &amp;#x3C;0&gt;&lt;br&gt;
vmhost kernel:   jiffies              &amp;#x3C;10fbc3871&gt;&lt;br&gt;
vmhost kernel:   next_to_watch.status &amp;#x3C;0&gt;&lt;br&gt;
vmhost kernel: MAC Status             &amp;#x3C;40080083&gt;&lt;br&gt;
vmhost kernel: PHY Status             &amp;#x3C;796d&gt;&lt;br&gt;
vmhost kernel: PHY 1000BASE-T Status  &amp;#x3C;7800&gt;&lt;br&gt;
vmhost kernel: PHY Extended Status    &amp;#x3C;3000&gt;&lt;br&gt;
vmhost kernel: PCI Status             &amp;#x3C;10&gt;&lt;br&gt;
vmhost kernel: e1000e 0000:00:1f.6 eth0: Reset adapter unexpectedly&lt;br&gt;
vmhost kernel: vmbr0: port 1(eth0) entered disabled state&lt;/p&gt;
&lt;p&gt;Aí ele resetava o adaptador de rede e, depois de um tempo, fazia tudo de novo, até a máquina ficar completamente offline.&lt;/p&gt;
&lt;p&gt;Procurando respostas na internet, encontrei &lt;a href=&quot;https://serverfault.com/questions/616485/e1000e-reset-adapter-unexpectedly-detected-hardware-unit-hang&quot;&gt;esta thread no ServerFault&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Causa&lt;/p&gt;
&lt;p&gt;Lendo várias fontes e listas de relatórios de bugs, parece que a melhor forma de reproduzir o problema é colocar o dispositivo numa situação de alta largura de banda, ou seja, transmitir grandes quantidades de dados que saturem a interface.&lt;/p&gt;
&lt;p&gt;No meu caso, isso costumava acontecer quando eu estava transmitindo mídia do servidor Plex local para algum dispositivo. Pela forma como a rede está montada, a VM Windows que roda a instância do Plex precisa buscar o arquivo de mídia em um compartilhamento de rede NFS em outro dispositivo, transcodificá-lo na VM e só então enviá-lo ao dispositivo de reprodução.&lt;/p&gt;
&lt;p&gt;Isso soma bastante tráfego de rede, geralmente ~40-100mbit/s, dependendo do dispositivo que reproduz o arquivo e da qualidade do arquivo de origem.&lt;/p&gt;
&lt;p&gt;O mesmo problema apareceu ao transmitir jogos via Steam Link para a nossa Apple TV. A conexão é cabeada, mas não é incomum a rede cair. Acho que está relacionado ao mesmo problema, mas vou ficar de olho para ver se acontece de novo depois da correção.&lt;/p&gt;
&lt;p&gt;Possível correção&lt;/p&gt;
&lt;p&gt;Parece que uma possível correção seria desabilitar &lt;strong&gt;GSO&lt;/strong&gt; &lt;em&gt;(Generic Segmentation Offload)&lt;/em&gt;, &lt;strong&gt;TSO&lt;/strong&gt; &lt;em&gt;(TCP Segmentation Offload)&lt;/em&gt; e &lt;strong&gt;GRO&lt;/strong&gt; &lt;em&gt;(Generic Receive Offload)&lt;/em&gt; na interface de rede*:&lt;/p&gt;
&lt;p&gt;ethtool -K eth0 gso off gro off tso off&lt;/p&gt;
&lt;p&gt;Apliquei isso na minha configuração e estou esperando para ver se realmente resolve o problema a longo prazo.&lt;/p&gt;
&lt;p&gt;Notas de rodapé&lt;/p&gt;
&lt;p&gt;* Essas opções têm a ver com delegar a segmentação de pacotes ao controlador da interface de rede para reduzir o uso de CPU da máquina. Mais detalhes podem ser encontrados &lt;a href=&quot;https://en.wikipedia.org/wiki/Large_send_offload&quot;&gt;neste artigo da Wikipédia&lt;/a&gt;.&lt;/p&gt;</content:encoded><category>Homelab</category><category>bandwidth</category><category>bug</category><category>network</category><category>nic</category><category>plex</category><category>proxmox</category><category>steam</category><category>streaming</category></item><item><title>Snippets: Executando um registro de imagens Docker local/privado em um swarm</title><link>https://bisko.be/pt/posts/dev/snippets-running-a-local-private-swarm-docker-image-registry/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/snippets-running-a-local-private-swarm-docker-image-registry/</guid><description>Hoje eu estava tentando gerar uma imagem Docker a partir de um projeto Node.js e queria fazer o deploy no meu swarm local de testes. Infelizmente, se alguém constrói uma imagem Docker local, essa imagem fica armazenada e acessível apenas no nó em que foi criada, e os outros nós do swarm não conseguem acessá-la. O</description><pubDate>Fri, 11 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Hoje eu estava tentando gerar uma imagem Docker a partir de um projeto Node.js e queria fazer o deploy no meu swarm local de testes.&lt;/p&gt;
&lt;p&gt;Infelizmente, se alguém constrói uma imagem Docker local, essa imagem fica armazenada e acessível apenas no nó em que foi criada, e os outros nós do swarm não conseguem acessá-la.&lt;/p&gt;
&lt;p&gt;A solução é enviar (push) a imagem para um registro Docker (registry), de onde ela pode ser baixada (pull) por todos os nós do swarm.&lt;/p&gt;
&lt;p&gt;Criando o registro&lt;/p&gt;
&lt;p&gt;Para instanciar um serviço de registro no swarm, execute:&lt;/p&gt;
&lt;p&gt;docker service create —name registry —publish 5000:5000 registry:latest&lt;/p&gt;
&lt;p&gt;Isso vai criar o serviço de registro e executá-lo no swarm, tornando-o acessível a todos os nós do swarm.&lt;/p&gt;
&lt;p&gt;Enviando uma imagem para o registro&lt;/p&gt;
&lt;p&gt;Para enviar uma imagem para o registro, você precisa fazer duas coisas – tagear e então fazer o push da imagem.&lt;/p&gt;
&lt;p&gt;Aqui vai um exemplo de como construir e tagear a imagem de uma vez só:&lt;/p&gt;
&lt;p&gt;docker build . -t localhost.localdomain:5000/&lt;image-name&gt;:latest&lt;/image-name&gt;&lt;/p&gt;
&lt;p&gt;E como enviá-la para o registro:&lt;/p&gt;
&lt;p&gt;docker push localhost.localdomain:5000/&lt;image-name&gt;:latestUsando a imagem em um serviço/container/etc&lt;/image-name&gt;&lt;/p&gt;
&lt;p&gt;Para usar a imagem em um serviço ou em um container, você especifica o caminho completo de cima, ou seja, &lt;code&gt;localhost.localdomain:5000/&amp;#x3C;image-name&gt;:latest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Os nós do swarm vão ter acesso à imagem e conseguirão baixá-la e executá-la.&lt;/p&gt;
&lt;p&gt;Nota importante sobre o endereço do registro&lt;/p&gt;
&lt;p&gt;Na maioria dos tutoriais por aí, a URL do registro aparece como &lt;code&gt;localhost:5000&lt;/code&gt;. Infelizmente isso não funciona e normalmente dá timeout quando se tenta usá-la. Se trocarmos para &lt;code&gt;localhost.localdomain&lt;/code&gt;, funciona!&lt;/p&gt;
&lt;p&gt;Não sei por que isso acontece, já que tanto &lt;code&gt;localhost&lt;/code&gt; quanto &lt;code&gt;localhost.localdomain&lt;/code&gt; resolvem para &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/p&gt;</content:encoded><category>Homelab</category><category>Snippets</category><category>Technology</category><category>docker</category><category>docker-swarm</category><category>registry</category><category>swarm</category></item><item><title>Snippets: Mapeie a tecla Section sign do teclado Apple para o acento grave ( § para ` )</title><link>https://bisko.be/pt/posts/dev/snippets-map-apple-keyboard-section-sign-key-to-back-quote-%C2%A7-to/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/snippets-map-apple-keyboard-section-sign-key-to-back-quote-%C2%A7-to/</guid><description>Já faz um tempo que uso os teclados da Apple, mas quando você mora na Europa não é fácil encontrar os com layout americano, que é o que estou acostumado a usar. No macOS existe um jeito &quot;fácil&quot; de remapear o teclado com apenas um comando no terminal: watch -n1 -x hidutil property -m ‘{“ProductID”:592, “VendorID”:1452}’ –set ‘{“UserKeyMapping”:[{“HIDKeyboardModifierMappingSrc”:0x700000035,”HIDKeyboardModifierMappingDst”:0x700000064}]}’ O que esse</description><pubDate>Wed, 31 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Já faz um tempo que uso os teclados da Apple, mas quando você mora na Europa não é fácil encontrar os com layout americano, que é o que estou acostumado a usar.&lt;/p&gt;
&lt;p&gt;No macOS existe um jeito “fácil” de remapear o teclado com apenas um comando no terminal:&lt;/p&gt;
&lt;p&gt;watch -n1 -x hidutil property -m ’{“ProductID”:592, “VendorID”:1452}’ —set ’{“UserKeyMapping”:[{“HIDKeyboardModifierMappingSrc”:0x700000035,“HIDKeyboardModifierMappingDst”:0x700000064}]}’&lt;/p&gt;
&lt;p&gt;O que esse trecho de código faz é mudar o mapeamento da tecla Section ( § ) para escrever a tecla de crase/til ( &lt;code&gt;`/ ~&lt;/code&gt; ).&lt;/p&gt;
&lt;p&gt;A parte &lt;code&gt;watch -n1 -x&lt;/code&gt; faz isso a cada segundo, porque às vezes o macOS gosta de perder a configuração, geralmente quando o computador entra em repouso ou quando o teclado é desconectado e conectado de novo.&lt;/p&gt;
&lt;p&gt;Note que, em algumas versões do macOS, o utilitário &lt;code&gt;watch&lt;/code&gt; não vem instalado e precisa ser obtido por meio de algo como &lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; ou &lt;a href=&quot;https://www.macports.org/&quot;&gt;MacPorts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;O código acima funciona com o teclado externo da Apple com USB e layout europeu com letras búlgaras. Pode ser necessário um pequeno ajuste nos valores hexadecimais das teclas para outros tipos de teclado.&lt;/p&gt;
&lt;p&gt;Uma variação do comando funciona bem para o mesmo tipo de teclado no próprio MacBook Pro. Basta trocar a posição dos valores &lt;code&gt;0x700000035&lt;/code&gt; e &lt;code&gt;0x700000064&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Andei pensando em escrever uma ferramenta para fazer isso automaticamente por mim, mas neste momento acho que vale mais a pena investir em um teclado novo com o layout certo 🙂&lt;/p&gt;</content:encoded><category>Uncategorized</category></item><item><title>Resetando/Reiniciando o controle remoto da Apple TV</title><link>https://bisko.be/pt/posts/dev/resetting-restarting-apple-tv-remote/</link><guid isPermaLink="true">https://bisko.be/pt/posts/dev/resetting-restarting-apple-tv-remote/</guid><description>Ontem à noite, durante a visita de uns convidados, consegui mergulhar o controle remoto da nossa Apple TV na água. Falta de sono e copos instáveis resultam em acidentes. O controle estava funcionando bem ontem à noite, mas hoje parou de funcionar. Não responde a nenhuma ação. Tentei bater na palma da mão, sacudir para tirar a água. Não ajudou em nada.</description><pubDate>Sun, 17 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ontem à noite, durante a visita de uns convidados, consegui mergulhar o controle remoto da nossa Apple TV na água. Falta de sono e copos instáveis resultam em acidentes.&lt;/p&gt;
&lt;p&gt;O controle estava funcionando bem ontem à noite, mas hoje parou de funcionar. Não responde a nenhuma ação. Tentei bater na palma da mão, sacudir para tirar a água. Não ajudou em nada.&lt;/p&gt;
&lt;p&gt;Achamos que ele tinha ido embora dessa para uma melhor e começamos a procurar o pote de arroz nos armários.&lt;/p&gt;
&lt;p&gt;Fui pesquisar na internet, mais como uma piada, para ver se dava para tentar “reiniciá-lo”.&lt;/p&gt;
&lt;p&gt;Um guia prestativo me orientou a pressionar e segurar os botões Aumentar Volume e Menu por 2-3 segundos para resetar o controle e colocá-lo em modo de pareamento.&lt;/p&gt;
&lt;p&gt;Fiquei muito surpreso quando &lt;strong&gt;realmente&lt;/strong&gt; &lt;strong&gt;funcionou&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;Então, se algo parecer estranho, tente resetar o controle primeiro. Se não funcionar, aí sim você pode partir para o arroz e a intervenção no hardware.&lt;/p&gt;
&lt;p&gt;TL;DR;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Segure Menu + Aumentar Volume por 2-3 segundos&lt;/li&gt;
&lt;li&gt;Espere alguns segundos&lt;/li&gt;
&lt;li&gt;Tente interagir com a Apple TV&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>Apple</category><category>Technology</category><category>AppleTV</category><category>Have you tried turning it off and on again?</category><category>remote</category><category>reset</category><category>restart</category></item></channel></rss>