[{"content":"🤔 Cos\u0026rsquo;è una syscall, in pratica? Crediti immagine: Mike Norris via Pexels\nOgni volta che il tuo programma legge un file, scrive su un socket o alloca memoria, a un certo punto deve chiedere al kernel di farlo per davvero. Quella richiesta è una syscall: il confine preciso e ben definito in cui il codice in userspace passa nel kernel. write(), read(), open(), mmap() non sono semplici funzioni di libreria, sono l\u0026rsquo;intero vocabolario che il tuo programma ha a disposizione per parlare con il mondo esterno. Tutto il resto — da fwrite a file.write() in Python fino a std::fs::File in Rust — non è altro che un wrapper attorno a questa manciata di punti di ingresso del kernel.\nQuesto è fondamentale perché si tratta anche dell\u0026rsquo;unico punto in cui l\u0026rsquo;evento \u0026ldquo;qualcosa è andato storto nel mondo esterno\u0026rdquo; può effettivamente riflettersi sul tuo programma. Un disco pieno, una connessione di rete caduta o il raggiungimento del limite massimo di file descriptor non sono bug nel tuo codice: sono fatti legati all\u0026rsquo;ambiente circostante, e il kernel te li comunica in un solo modo, ovvero tramite una syscall che restituisce un errore. Il sistema non mente, ti sta semplicemente dicendo con onestà: \u0026ldquo;No, in questo momento non posso eseguire questa operazione\u0026rdquo;. Se poi il tuo codice quel \u0026ldquo;no\u0026rdquo; lo ascolta davvero, beh, questa è tutta un\u0026rsquo;altra storia.\n💥 Perché rompere le cose apposta? Ecco la scomoda verità: il codice di gestione degli errori è, quasi sempre, il codice meno testato di tutta la codebase. Puoi scrivere un blocco except OSError perfettamente sensato attorno a una chiamata write(), ma se il disco non si riempie mai durante la fase di test, quel ramo di codice non verrà mai eseguito. Finisce così in produzione senza essere mai stato testato, ed entrerà in funzione per la prima volta proprio davanti a un utente reale, magari mettendo a rischio dati di produzione veri.\nI fallimenti reali come \u0026ldquo;disco pieno\u0026rdquo; o \u0026ldquo;connessione interrotta\u0026rdquo; sono rari e scomodi da riprodurre a comando. Il fault injection li rende economici e ripetibili. Invece di sperare che il tuo codice di gestione degli errori funzioni, forzi tu stesso il fallimento, quando vuoi tu, e guardi cosa succede davvero.\nEsiste uno strumento nativo nel kernel per farlo, /sys/kernel/debug/fail_function, ma richiede un kernel compilato con il supporto al fault injection per il debug, un\u0026rsquo;opzione che non è sempre abilitata di default. La buona notizia è che, per intercettare il confine delle syscall, esiste uno strumento con una barriera d\u0026rsquo;accesso molto più bassa e probabilmente già installato sul tuo sistema.\n🧪 Ecco progress_log Ogni tecnica di fault injection di questa serie ha il suo piccolo target costruito su misura, niente framework, niente codebase condivisa da studiare prima. Per questo esperimento con strace useremo qualcosa di estremamente semplice.\nEcco progress_log. Il programma elabora un batch di \u0026ldquo;elementi\u0026rdquo;, registrando una riga per ciascuno di essi in un file di log man mano che procede. Si tratta del classico log di checkpoint di cui ogni job batch ha bisogno: se il processo muore a metà strada, saprai esattamente dove si è interrotto.\n#!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34;progress_log: record how far a (simulated) batch job got, one line per item. If the job dies partway through, this log tells you exactly which item it was on -- but only if a failed write() is caught instead of crashing silently. Opened unbuffered (buffering=0) on purpose: Python\u0026#39;s normal buffered file objects can silently retry -- and succeed -- writing data on close(), even after your code already caught and reported a write() failure for it. Unbuffered means what you see here is exactly what happens at the syscall level, no surprises. \u0026#34;\u0026#34;\u0026#34; import sys def main() -\u0026gt; int: if len(sys.argv) != 3: print(f\u0026#34;usage: {sys.argv[0]} \u0026lt;count\u0026gt; \u0026lt;path\u0026gt;\u0026#34;, file=sys.stderr) return 1 count = int(sys.argv[1]) path = sys.argv[2] with open(path, \u0026#34;wb\u0026#34;, buffering=0) as log: for item in range(1, count + 1): try: log.write(f\u0026#34;processed item {item}\\n\u0026#34;.encode()) print(f\u0026#34;processed item {item}\u0026#34;, flush=True) except OSError as e: print(f\u0026#34;stopped at item {item}: {e}\u0026#34;, file=sys.stderr) return 1 print(f\u0026#34;done: processed {count} items\u0026#34;, flush=True) return 0 if __name__ == \u0026#34;__main__\u0026#34;: sys.exit(main()) La docstring mette in evidenza un dettaglio su cui vale la pena soffermarsi, anche in un articolo che mira alla massima semplicità. Una normale chiamata open(path, \u0026quot;w\u0026quot;) bufferizza le scritture e, in caso di fallimento, Python non scarta necessariamente i byte non scritti. Una chiamata a close() successiva potrebbe infatti riuscire a scaricarli sul disco (flushing) comunque, dopo che il tuo codice ha già segnalato all\u0026rsquo;utente il fallimento dell\u0026rsquo;operazione. L\u0026rsquo;opzione buffering=0 evita tutto questo alla radice: ogni singola operazione di scrittura si traduce immediatamente in una syscall, senza tentativi di riprovo o buffering nascosti. Si tratta di una trappola insidiosa da conoscere a prescindere dal fault injection, ma che in questo contesto assume un\u0026rsquo;importanza cruciale.\nIl blocco except OSError rappresenta la nostra gestione degli errori, proprio quella parte di codice che normalmente resta ineseguita. Vediamo come farla scattare. Non è necessario alcun passaggio di compilazione:\n$ chmod +x progress_log.py $ ./progress_log.py 3 progress.log processed item 1 processed item 2 processed item 3 done: processed 3 items Tracciando l\u0026rsquo;esecuzione vediamo esattamente quello che ci aspettiamo: una chiamata write() sul file di log (fd 3) alternata a una write() su stdout (fd 1) per stampare il progresso.\n$ strace -o baseline.strace.log -e trace=write ./progress_log.py 3 progress.log $ cat baseline.strace.log write(3, \u0026#34;processed item 1\\n\u0026#34;, 17) = 17 write(1, \u0026#34;processed item 1\\n\u0026#34;, 17) = 17 write(3, \u0026#34;processed item 2\\n\u0026#34;, 17) = 17 write(1, \u0026#34;processed item 2\\n\u0026#34;, 17) = 17 write(3, \u0026#34;processed item 3\\n\u0026#34;, 17) = 17 write(1, \u0026#34;processed item 3\\n\u0026#34;, 17) = 17 write(1, \u0026#34;done: processed 3 items\\n\u0026#34;, 24) = 24 +++ exited with 0 +++ 🔍 Prima sorpresa: strace --inject strace sa far fallire le syscall a comando fin dalla versione 4.15 (dicembre 2016, frutto di un lavoro guidato da Dmitry Levin nato come progetto GSoC nel 2016). Nessuna patch al kernel richiesta:\n$ strace -e trace=write -e inject=write:error=ENOSPC:when=3 ./prog when=3 significa \u0026ldquo;fai fallire la terza invocazione di questa syscall.\u0026rdquo; Puntiamolo su progress_log e simuliamo un disco pieno alla terza write():\n$ strace -o naive.strace.log -e trace=write -e inject=write:error=ENOSPC:when=3 ./progress_log.py 5 progress.log processed item 1 stopped at item 2: [Errno 28] No space left on device Un attimo: l\u0026rsquo;esecuzione si è bloccata all\u0026rsquo;elemento 2, non al 3. Abbiamo forse sbagliato a contare? Il log di strace ci dice di no:\n$ cat naive.strace.log write(3, \u0026#34;processed item 1\\n\u0026#34;, 17) = 17 write(1, \u0026#34;processed item 1\\n\u0026#34;, 17) = 17 write(3, \u0026#34;processed item 2\\n\u0026#34;, 17) = -1 ENOSPC (No space left on device) (INJECTED) write(2, \u0026#34;stopped at item 2: [Errno 28] No\u0026#34;..., 54) = 54 +++ exited with 1 +++ Eccolo. when=3 conta la terza syscall write() che il processo fa, punto, e anche il nostro messaggio di avanzamento su stdout (processed item 1, fd 1) è una write(). Al kernel non importa (e non può sapere) che la nostra intenzione era quella di colpire solo il file di log: il filtro -e inject=write:... intercetta la syscall write() su qualsiasi file descriptor. Questa è la classica assunzione che trae in inganno chiunque si avvicini al fault injection per la prima volta: il fallimento iniettato è reale, ma non avviene nel punto in cui ci si aspetterebbe.\n🎯 Delimitare il raggio d\u0026rsquo;azione con -P Per ovviare a questo problema, strace permette di restringere l\u0026rsquo;ambito del tracciamento — e di riflesso anche il contatore delle iniezioni — alle sole syscall che interessano un percorso specifico, utilizzando l\u0026rsquo;opzione -P. Nota bene: su strace 7.1, l\u0026rsquo;uso di un percorso relativo non produce alcun match, in modo del tutto silenzioso. Nessuna write() viene tracciata né iniettata, e il programma viene eseguito fino alla fine senza interruzioni, anche se la directory corrente corrisponde esattamente a quella del file.\n$ strace -o relative.strace.log -e trace=write -P progress.log \\ -e inject=write:error=ENOSPC:when=3 ./progress_log.py 5 progress.log processed item 1 processed item 2 processed item 3 processed item 4 processed item 5 done: processed 5 items Usare il percorso assoluto risolve il problema:\n$ strace -o scoped.strace.log -e trace=write -P \u0026#34;$(pwd)/progress.log\u0026#34; \\ -e inject=write:error=ENOSPC:when=3 ./progress_log.py 5 progress.log processed item 1 processed item 2 stopped at item 3: [Errno 28] No space left on device Ora solo le chiamate write() dirette al file descriptor del log vengono tracciate e conteggiate. Di conseguenza, il parametro when=3 assume finalmente il significato desiderato:\n$ cat scoped.strace.log write(3, \u0026#34;processed item 1\\n\u0026#34;, 17) = 17 write(3, \u0026#34;processed item 2\\n\u0026#34;, 17) = 17 write(3, \u0026#34;processed item 3\\n\u0026#34;, 17) = -1 ENOSPC (No space left on device) (INJECTED) +++ exited with 1 +++ $ cat progress.log processed item 1 processed item 2 I primi due elementi sono scritti correttamente sul disco, il terzo è stato rifiutato esattamente come avverrebbe in caso di disco pieno, e il nostro blocco except OSError ha intercettato l\u0026rsquo;errore in modo pulito. Nessun traceback Python spaventoso, nessun file corrotto silenziosamente, ma solo il messaggio di errore previsto per questa evenienza. Abbiamo appena visto il nostro ramo di gestione degli errori entrare in funzione per la prima volta.\n🧫 Da guardare a verificare Osservare l\u0026rsquo;output sul terminale è perfetto per un articolo di blog, ma è del tutto inutile all\u0026rsquo;interno di una suite di test. Il vero obiettivo, infatti, non è assistere passivamente al fallimento, bensì automatizzare la verifica che il codice risponda correttamente all\u0026rsquo;errore. Trasformiamo quindi il nostro esperimento manuale in un test automatizzato in grado di far fallire una build in caso di regressione:\n#!/usr/bin/env python3 \u0026#34;\u0026#34;\u0026#34;Assert progress_log\u0026#39;s unhappy path: a disk-full write() must be caught, reported, and must not corrupt entries already on disk. This is the point of the whole exercise -- not just watching the failure happen, asserting on it.\u0026#34;\u0026#34;\u0026#34; import os import subprocess import sys import tempfile HERE = os.path.dirname(os.path.abspath(__file__)) def run_with_injected_fault(count, path, when): abs_path = os.path.abspath(path) return subprocess.run( [ \u0026#34;strace\u0026#34;, \u0026#34;-e\u0026#34;, \u0026#34;trace=write\u0026#34;, \u0026#34;-P\u0026#34;, abs_path, \u0026#34;-e\u0026#34;, f\u0026#34;inject=write:error=ENOSPC:when={when}\u0026#34;, sys.executable, os.path.join(HERE, \u0026#34;progress_log.py\u0026#34;), str(count), path, ], capture_output=True, text=True, ) def main(): with tempfile.TemporaryDirectory() as tmp: path = os.path.join(tmp, \u0026#34;progress.log\u0026#34;) result = run_with_injected_fault(count=5, path=path, when=3) assert result.returncode == 1, f\u0026#34;expected exit code 1, got {result.returncode}\u0026#34; assert \u0026#34;stopped at item 3\u0026#34; in result.stderr, ( f\u0026#34;expected a failure message for item 3 on stderr, got: {result.stderr!r}\u0026#34; ) assert \u0026#34;No space left on device\u0026#34; in result.stderr, ( f\u0026#34;expected ENOSPC in the error message, got: {result.stderr!r}\u0026#34; ) with open(path) as f: lines = f.readlines() assert len(lines) == 2, f\u0026#34;expected exactly 2 items to survive, found {len(lines)}\u0026#34; print(\u0026#34;PASS: progress_log detects and reports a failed write(), and stops without corrupting the log\u0026#34;) if __name__ == \u0026#34;__main__\u0026#34;: main() $ python3 test_progress_log.py PASS: progress_log detects and reports a failed write(), and stops without corrupting the log Non servono framework di test complessi o fixture pesanti. Sono sufficienti subprocess e delle semplici istruzioni assert per avviare strace esattamente come abbiamo fatto da riga di comando, verificando i tre aspetti fondamentali che qualunque code reviewer esigerebbe: il processo deve terminare con un codice di errore, deve stampare una spiegazione chiara a terminale e deve lasciare il file di log in uno stato integro (contenente esattamente i primi 2 elementi validi, senza righe troncate o dati corrotti). Questo è il classico test dell\u0026rsquo;unhappy path che prima mancava del tutto, applicato a un codice che finora non aveva mai dovuto dimostrare la propria robustezza sul campo. Inserendo questo controllo nella pipeline di CI (prestando attenzione alle note sui container descritte più avanti), questo genere di regressioni verrà intercettato tempestivamente a ogni futura modifica di progress_log.py.\n🤨 Perché non usare semplicemente un mock? Si tratta di un\u0026rsquo;obiezione legittima. Si potrebbe usare semplicemente unittest.mock.patch(\u0026quot;builtins.open\u0026quot;) per sollevare manualmente un OSError e verificare che venga intercettato correttamente. Niente strace, niente subprocess, nessun problema con ptrace in ambiente CI. Sembra una via molto più semplice e lineare, e in effetti lo è.\nIl punto è che in questo modo si sta testando uno scenario completamente diverso. Se usi un mock per simulare l\u0026rsquo;errore, ti stai limitando a verificare che il blocco except OSError intercetti l\u0026rsquo;eccezione che tu stesso hai sollevato artificialmente un istante prima. Questo test è corretto per costruzione, ma non garantisce affatto che il codice si comporti bene quando è la vera syscall write() a fallire a livello di sistema operativo. Un mock, infatti, non simula minimamente la complessità del reale stack di I/O.\nQuesta differenza non è teorica. Ci ha già fregati una volta in questo stesso post. Il buffer che silenziosamente ritenta la scrittura su close(), l\u0026rsquo;intero motivo per cui progress_log apre il file con buffering=0, si manifesta solo quando una write() vera fallisce per davvero, dentro il vero livello di buffering di Python. Se mocki open(), quel livello semplicemente non fa più parte del test. Il problema resta invisibile e il bug finisce comunque in produzione.\nIl fault injection operato direttamente a livello di syscall non richiede alcuna modifica o predisposizione nel codice dell\u0026rsquo;applicazione. Non ci sono punti di ingresso da patchare o configurare, e funziona allo stesso modo sia che si stia testando un subprocess, una libreria esterna o un binario precompilato di cui non si possiedono i sorgenti. Se il mocking si rivela uno strumento formidabile per testare la logica di business pura, non è in grado di dirci nulla su come il nostro software interagisce con il kernel attraverso il confine delle syscall — ed è proprio questo confine il fulcro di tutta questa serie.\n⚠️ Qualche importante avvertenza Ecco due aspetti pratici a cui prestare molta attenzione (ed entrambi ci hanno fatto sbattere la testa in passato):\nCircoscrivi sempre il fault injection con precisione. L\u0026rsquo;esecuzione senza un ambito (scope) definito di prima non è fallita nel punto esatto che volevamo testare: è fallita in un punto reale del sistema, solo che non era quello da noi scelto. Se lanci --inject contro un servizio di produzione o critico, un filtro non ben delimitato rischia di compromettere percorsi di esecuzione del codice che non avevi alcuna intenzione di toccare. Usa sempre l\u0026rsquo;opzione -P (o un filtro -e trace= molto restrittivo) prima di avviare questo strumento su contesti più complessi o delicati di un semplice binario di test.\nstrace richiede ptrace, una syscall che i container tendono a bloccare per motivi di sicurezza. strace deve essere eseguito con lo stesso UID del processo target o disporre della capability CAP_SYS_PTRACE. Inoltre, il profilo seccomp predefinito di Docker blocca ptrace a prescindere. Di conseguenza, per far funzionare tutto questo all\u0026rsquo;interno di un container standard (aspetto fondamentale se intendi integrare questi test in una pipeline di CI/CD), dovrai avviarlo usando l\u0026rsquo;opzione --cap-add=SYS_PTRACE oppure --security-opt seccomp=unconfined.\nAffronteremo la questione dell\u0026rsquo;overhead più avanti nella serie, quando confronteremo questo approccio con seccomp ed eBPF.\n🏁 Tirando le somme In questo articolo abbiamo esplorato la reale natura di una syscall, compreso l\u0026rsquo;utilità di simularne il fallimento e implementato il nostro primo meccanismo di fault injection funzionante senza dover scrivere una sola riga di codice di test invasivo. Tutto il lavoro pesante è affidato a strace, mentre progress_log.py funge da bersaglio ideale. Abbiamo affrontato e risolto un\u0026rsquo;insidia comune legata al modo in cui viene conteggiata la \u0026ldquo;terza chiamata\u0026rdquo; e, anziché accontentarci del classico \u0026ldquo;funziona sulla mia macchina\u0026rdquo;, abbiamo consolidato l\u0026rsquo;esperimento in un test automatizzato pronto per intercettare qualsiasi regressione futura. Dopotutto, il vero traguardo non era semplicemente osservare una chiamata write() che fallisce, ma dimostrare empiricamente che la nostra applicazione è in grado di gestire la situazione d\u0026rsquo;emergenza con eleganza.\nNel prossimo articolo abbandoneremo strace per scrivere il nostro personalissimo tool di iniezione basato su ptrace. Questo ci aprirà la strada a scenari di errore complessi che l\u0026rsquo;opzione --inject standard non è in grado di riprodurre, come ad esempio una chiamata write() che restituisce un successo parziale scrivendo solo una porzione del buffer. Per quell\u0026rsquo;esperimento selezioneremo il target ideale per metterne in luce i dettagli di funzionamento, senza vincolarci necessariamente all\u0026rsquo;applicazione usata oggi.\nIl sorgente completo di questo capitolo è quello inserito sopra. progress_log.py e test_progress_log.py, senza modifiche, sono ciò che ha generato ogni singolo output di questo post.\n📚 Crediti e approfondimenti La pagina man di strace(1) documenta per intero i flag --inject/-e inject=. Il talk di Dmitry Levin al FOSDEM 2017 sul fault injection di strace racconta lo stesso lavoro dal punto di vista del maintainer, insieme al progetto GSoC del 2016 che lo ha fatto partire. E se questo post ti ha incuriosito, i miei articoli precedenti coprono un terreno simile: Expect the unexpected parla di guasti al disco simulati con device mapper, e Fault Injection in Network Namespace and Veth Environments fa lo stesso con netem.\nBuon (fault) hacking!\n","permalink":"https://ilmanzo.github.io/it/post/teaching-write-to-say-no/","summary":"\u003ch2 id=\"-cosè-una-syscall-in-pratica\"\u003e🤔 Cos\u0026rsquo;è una syscall, in pratica?\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"Una Mini Cooper blu in equilibrio su due ruote durante uno spettacolo di stunt driving\" loading=\"lazy\" src=\"/img/stunt-mini-cooper.jpg\"\u003e\nCrediti immagine: \u003ca href=\"https://www.pexels.com/@miken/\"\u003eMike Norris\u003c/a\u003e via \u003ca href=\"https://www.pexels.com/photo/exciting-car-stunt-show-at-saltburn-by-the-sea-34153581/\"\u003ePexels\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eOgni volta che il tuo programma legge un file, scrive su un socket o alloca memoria, a un certo punto deve chiedere al kernel di farlo per davvero. Quella richiesta è una \u003cstrong\u003esyscall\u003c/strong\u003e: il confine preciso e ben definito in cui il codice in userspace passa nel kernel. \u003ccode\u003ewrite()\u003c/code\u003e, \u003ccode\u003eread()\u003c/code\u003e, \u003ccode\u003eopen()\u003c/code\u003e, \u003ccode\u003emmap()\u003c/code\u003e non sono semplici funzioni di libreria, sono l\u0026rsquo;intero vocabolario che il tuo programma ha a disposizione per parlare con il mondo esterno. Tutto il resto — da \u003ccode\u003efwrite\u003c/code\u003e a \u003ccode\u003efile.write()\u003c/code\u003e in Python fino a \u003ccode\u003estd::fs::File\u003c/code\u003e in Rust — non è altro che un wrapper attorno a questa manciata di punti di ingresso del kernel.\u003c/p\u003e","title":"syscalln't 🚫"},{"content":"📝 In breve (TL;DR) Siamo già a metà del 2026 e questi primi sei mesi sono stati davvero intensi. Tra scrittura di codice, conferenze e workshop in tutta Europa, ho lavorato sulla gestione dei sistemi, sul green computing, sull\u0026rsquo;IA locale e sulla sicurezza del software. Questo post è un riepilogo dei progetti e dei talk che hanno caratterizzato il mio percorso da febbraio a giugno.\n🛠️ Da febbraio a maggio: approfondimento sul progetto Uyuni Per i primi quattro mesi di quest\u0026rsquo;anno, la mia attività principale si è concentrata quasi interamente sull\u0026rsquo;affascinante mondo del progetto Uyuni. Se non avete ancora avuto l\u0026rsquo;occasione di usarlo, Uyuni è una soluzione di gestione delle infrastrutture e di configurazione incredibilmente potente e completamente open-source. Originariamente nato come evoluzione di Spacewalk, oggi rappresenta il progetto comunitario upstream che alimenta direttamente SUSE Multi-Linux Manager, precedentemente noto come SUSE Manager.\nLa vera magia di Uyuni sta nella sua profonda integrazione con Salt, noto anche come SaltStack. Questa integrazione consente agli amministratori di gestire, applicare patch e configurare migliaia di macchine in tempo reale. È un vero e proprio salvavita quando si affrontano ambienti complessi e di grandi dimensioni, perché può gestire qualsiasi cosa: dalla distribuzione automatizzata dei pacchetti ai controlli di sicurezza tramite OpenSCAP. Ciò che lo rende ancora più interessante è il supporto multi-distribuzione, che permette di gestire openSUSE, SUSE Linux Enterprise, Red Hat Enterprise Linux, Rocky Linux, Debian e Ubuntu da un\u0026rsquo;unica dashboard unificata.\nOltre a tutto il lavoro su Salt e sui pacchetti, ho avuto il piacere di condurre una sessione interna di condivisione delle conoscenze su SELinux. Spesso SELinux può sembrare una specie di \u0026ldquo;magia oscura\u0026rdquo; per gli amministratori di sistema, quindi abbiamo analizzato casi reali di risoluzione dei problemi, gestione delle etichette e generazione di policy, demistificando il modo in cui protegge la nostra infrastruttura e i canali di comunicazione con i client. È stato davvero gratificante vedere i colleghi acquisire sicurezza nella gestione delle policy di sicurezza anziché limitarsi a impostare SELinux in modalità permissive.\nDate un\u0026rsquo;occhiata al sito web del progetto Uyuni e alla repository GitHub di Uyuni per saperne di più.\n🌿 Dal 20 al 25 aprile: SUSECON a Praga e openSUSE Developer Summit Verso la fine di aprile, ho preparato le valigie e sono partito alla volta della splendida città di Praga per partecipare a SUSECON 2026. È stato un viaggio doppiamente speciale, perché l\u0026rsquo;openSUSE Developer Summit (noto anche come ODS 2026) si è svolto in contemporanea con la conferenza principale. Questa combinazione ha creato una fantastica sinergia in cui gli sviluppatori enterprise e i membri della community open-source hanno potuto incontrarsi, scambiarsi idee e collaborare alla prossima generazione di tecnologie Linux.\nGreen computing e innovazione comunitaria Tra i molti temi ispiratori discussi durante l\u0026rsquo;evento, il green computing e la sostenibilità ambientale nell\u0026rsquo;IT mi stavano particolarmente a cuore, ed erano anche il fulcro del mio talk. Con i moderni data center che consumano una quota enorme e in continua crescita dell\u0026rsquo;elettricità globale, trovare modi per rendere le nostre infrastrutture più efficienti dal punto di vista energetico è diventato un\u0026rsquo;assoluta necessità. Durante le presentazioni e nei corridoi abbiamo avuto discussioni tecniche molto approfondite su come ottimizzare tutto, dal kernel Linux ai carichi di lavoro containerizzati, per ridurre al minimo l\u0026rsquo;impronta di carbonio.\nSe volete saperne di più sull\u0026rsquo;evento, potete visitare il sito ufficiale di SUSECON 2026 all\u0026rsquo;indirizzo https://www.susecon.com/ e trovare maggiori dettagli sulla pagina dell\u0026rsquo;openSUSE Developer Summit su https://events.opensuse.org/conferences/ODS26. Ho anche caricato le slide della mia presentazione sul green computing e sull\u0026rsquo;ottimizzazione dei sistemi, che potete visualizzare qui.\n🐧 23 maggio: Linux Day SE Mantova (Flatpak e Distrobox) A fine maggio ho avuto la splendida opportunità di tornare agli eventi delle community locali parlando al Linux Day Special Edition nella storica città di Mantova. C\u0026rsquo;è sempre qualcosa di incredibilmente speciale nei gruppi di utenti locali (LUG), e questo evento non ha fatto eccezione, pieno di utenti entusiasti, sviluppatori e appassionati di open source. Il mio talk in questa sessione si è concentrato su come modernizzare la distribuzione delle applicazioni desktop e i nostri spazi di lavoro di sviluppo usando Flatpak e Distrobox.\nPerché Flatpak + Distrobox? Flatpak ha completamente cambiato le regole del gioco per le applicazioni desktop su Linux fornendo un formato di pacchettizzazione sicuro, isolato e indipendente dalla distribuzione. Risolve efficacemente il vecchio problema dei conflitti di dipendenze, il che significa che è possibile eseguire le ultime app desktop su qualsiasi distribuzione senza preoccuparsi di rompere le librerie di sistema. Dall\u0026rsquo;altro lato, Distrobox è uno strumento fantastico per il lavoro da riga di comando. Consente di eseguire qualsiasi distribuzione Linux all\u0026rsquo;interno del terminale utilizzando container tramite Podman o Docker. Questo significa che uno sviluppatore può eseguire senza problemi strumenti e librerie di Arch, Fedora, Debian o Ubuntu direttamente sul proprio sistema host senza intasare il sistema operativo principale.\nCombinando queste due tecnologie, si ottiene un ambiente altamente flessibile e incredibilmente robusto, perfetto per i moderni sistemi operativi immutabili. Mi sono divertito moltissimo a mostrare come funzionano insieme questi strumenti, dimostrando quanto sia facile configurare un intero ambiente di sviluppo in pochi secondi.\nPotete trovare maggiori informazioni sulla sede e sugli organizzatori, insieme alle slide e alle registrazioni, sulla pagina dell\u0026rsquo;evento sul sito del Linux Day Mantova.\n🤖 Dal 25 al 29 maggio: Workshop AI a Norimberga Subito dopo l\u0026rsquo;evento a Mantova, sono andato a Norimberga, in Germania, per trascorrere un\u0026rsquo;intera settimana in un workshop intensivo sull\u0026rsquo;intelligenza artificiale. Poiché i voli erano un po\u0026rsquo; complicati da incastrare, sono atterrato prima a Monaco e poi ho preso un treno panoramico per Norimberga. È stato un piacevole passaggio dal sole italiano alle verdi colline bavaresi.\nSul piano tecnico, questo workshop è stato un viaggio approfondito nell\u0026rsquo;intelligenza artificiale locale (local-first), nei modelli linguistici con pesi aperti (open-weight) e nelle sfide pratiche legate alla distribuzione di questi modelli sul proprio hardware. Abbiamo trascorso cinque giorni intensi a lavorare su diverse architetture IA all\u0026rsquo;avanguardia. Uno dei nostri principali ambiti di interesse è stato la quantizzazione dei modelli, riducendone le dimensioni con formati come GGUF ed EXL2 per poter eseguire modelli enormi da 70 miliardi di parametri su hardware di livello consumer o su piccoli server locali. Abbiamo anche dedicato molto tempo alla creazione di pipeline di ricerca di documenti a bassa latenza utilizzando la generazione aumentata da recupero, nota come RAG (Retrieval-Augmented Generation), integrata con database vettoriali ad alte prestazioni.\n⚡ 6 giugno: GDG DevFest Vicenza (5+1 funzionalità di systemd da conoscere assolutamente) All\u0026rsquo;inizio di giugno sono rimasto più vicino a casa e mi sono unito alla fantastica community del GDG DevFest Vicenza 2026, nella splendida cornice del Veneto. Sono stato invitato a tenere il talk di chiusura e ho scelto un tema a volte controverso nella community Linux: systemd. La mia presentazione si intitolava \u0026ldquo;5+1 funzionalità di systemd da conoscere assolutamente\u0026rdquo;.\nMolti sviluppatori tendono a pensare a systemd solo come a uno strumento di base per avviare e arrestare i servizi, ma in realtà racchiude una miniera di funzionalità integrate che possono sostituire molte complicate utilità esterne. Nel mio talk ho illustrato al pubblico sei dei miei trucchi preferiti. Abbiamo discusso di systemd-socket-activate per configurare l\u0026rsquo;avvio dei servizi su richiesta e abbiamo visto come systemd-analyze plot possa generare un bellissimo grafico visivo per ottimizzare i tempi di avvio del sistema. Abbiamo anche parlato dell\u0026rsquo;uso di DynamicUser per eseguire i servizi con utenti del tutto isolati ed effimeri a vantaggio della massima sicurezza, e di come utilizzare i file drop-in per estendere i servizi senza modificarne la configurazione principale. Infine, abbiamo visto come systemd gestisce le directory standard tramite RuntimeDirectory o StateDirectory e abbiamo parlato di systemd-sysext, che consente estensioni di sistema fluide e non distruttive.\nPer darvi un\u0026rsquo;idea di quanto sia semplice configurare un servizio sicuro con funzionalità di sandboxing usando systemd, ecco un rapido esempio di file di servizio che abilita DynamicUser e imposta automaticamente directory di stato isolate:\n[Unit] Description=My secure local sandbox service [Service] ExecStart=/usr/bin/my-cool-app DynamicUser=yes StateDirectory=my-cool-app RuntimeDirectory=my-cool-app ProtectSystem=strict ProtectHome=yes Con queste poche righe, systemd gestisce un utente completamente effimero, isola le directory home, blocca l\u0026rsquo;accesso in scrittura al sistema e monta directory di runtime e di stato pulite per l\u0026rsquo;applicazione. L\u0026rsquo;energia alla DevFest è stata incredibile, e ho avuto ottimi scambi con sviluppatori web e mobile, sorpresi nel vedere quanto systemd potesse semplificare le loro pipeline di distribuzione. Potete visitare il sito del GDG DevFest Vicenza all\u0026rsquo;indirizzo https://devfest.gdgvicenza.it/.\n🔒 Dall'8 all'11 giugno: Workshop sulla sicurezza a Helsinki Per concludere questa intensa stagione di viaggi, ho trascorso la seconda settimana di giugno a Helsinki, in Finlandia, partecipando all\u0026rsquo;Helsinki Security Workshop. Questo evento ha riunito sistemisti, ricercatori di sicurezza e sviluppatori principali provenienti da tutta Europa.\nAndare così a nord in Finlandia a giugno è stato un vero piacere, soprattutto perché siamo stati accolti da un clima estivo insolitamente caldo e piacevole. La fresca brezza proveniente dal Mar Baltico era incredibilmente rigenerante. Essendo la stagione delle famose \u0026ldquo;notti bianche\u0026rdquo; del Nord, in cui il sole quasi non tramonta e le serate rimangono luminose e radiose, avevamo a disposizione infinite ore di luce. Ne abbiamo approfittato appieno con splendide attività di squadra all\u0026rsquo;aperto.\n🌅 Uno sguardo al futuro Ripensando a questi primi sei mesi del 2026, mi sento grato per tutte le opportunità che ho avuto di imparare, programmare, viaggiare e scambiare idee. Questa prima metà dell\u0026rsquo;anno ha evidenziato quanto i tre pilastri dell\u0026rsquo;automazione, della sostenibilità ambientale e della sicurezza profonda siano fondamentali per il futuro del nostro settore.\nVoglio rivolgere un enorme ringraziamento a tutti gli organizzatori, ai volontari, ai colleghi relatori e ai partecipanti che hanno reso questi eventi così gratificanti e memorabili. La community open-source è davvero un luogo speciale, e sono le persone a renderla così dinamica e stimolante.\nUn ringraziamento speciale va ad alcune delle community locali che hanno reso quest\u0026rsquo;anno memorabile. È stato fantastico collaborare e scambiare idee con gli amici di GDG Vicenza, GDG Venezia, BacaroTech e Mantova Dev, oltre a partecipare alla bellissima iniziativa copiaIncolla Open. Questi gruppi locali sono il vero cuore pulsante dell\u0026rsquo;open source, e l\u0026rsquo;energia, la curiosità e il calore che portano in ogni singolo incontro e discussione sono incredibilmente stimolanti.\nOra che la stagione primaverile delle conferenze si è conclusa, ho in programma di tornare alla mia scrivania per dedicare del tempo prezioso a scrivere codice e fare esperimenti.\nPartecipa alla conversazione! Mi piacerebbe molto conoscere le vostre esperienze e idee su questi argomenti. Parliamone nella sezione dei commenti qui sotto:\nModelli IA locali: Avete provato a eseguire modelli linguistici di grandi dimensioni in locale sul vostro computer? Quali sono i vostri strumenti e formati di quantizzazione preferiti? Ambiente di sviluppo: Usate Flatpak o Distrobox nei vostri flussi di lavoro quotidiani? Come ha influito sulla stabilità del vostro ambiente? Funzionalità avanzate di systemd: Sfruttate le funzionalità avanzate di systemd come DynamicUser o sysext nei vostri servizi, o lo considerate ancora un semplice gestore di servizi di base? Se preferite, potete anche contattarmi direttamente su Mastodon per condividere feedback o fare domande. Continuiamo la nostra conversazione sull\u0026rsquo;open source!\n","permalink":"https://ilmanzo.github.io/it/post/mid-2026-recap/","summary":"\u003ch2 id=\"-in-breve-tldr\"\u003e📝 In breve (TL;DR)\u003c/h2\u003e\n\u003cp\u003eSiamo già a metà del 2026 e questi primi sei mesi sono stati davvero intensi. Tra scrittura di codice, conferenze e workshop in tutta Europa, ho lavorato sulla gestione dei sistemi, sul green computing, sull\u0026rsquo;IA locale e sulla sicurezza del software. Questo post è un riepilogo dei progetti e dei talk che hanno caratterizzato il mio percorso da febbraio a giugno.\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"-da-febbraio-a-maggio-approfondimento-sul-progetto-uyuni\"\u003e🛠️ Da febbraio a maggio: approfondimento sul progetto Uyuni\u003c/h2\u003e\n\u003cp\u003ePer i primi quattro mesi di quest\u0026rsquo;anno, la mia attività principale si è concentrata quasi interamente sull\u0026rsquo;affascinante mondo del progetto Uyuni. Se non avete ancora avuto l\u0026rsquo;occasione di usarlo, Uyuni è una soluzione di gestione delle infrastrutture e di configurazione incredibilmente potente e completamente open-source. Originariamente nato come evoluzione di Spacewalk, oggi rappresenta il progetto comunitario upstream che alimenta direttamente SUSE Multi-Linux Manager, precedentemente noto come SUSE Manager.\u003c/p\u003e","title":"Riepilogo di metà 2026: programmazione, conferenze e workshop"},{"content":"🧭 Dove eravamo rimasti Benvenuti al nuovo capitolo della nostra serie dedicata alla misurazione della copertura dei test per i programmi binari!\nNella parte 1 abbiamo utilizzato il flag -cover integrato in Go: pulito e preciso, ma funziona solo se si possiede il codice sorgente e lo si può ricompilare. Nella parte 2 abbiamo usato valgrind e gdb per tracciare gzip senza toccarne il sorgente. Nella parte 3 abbiamo esplorato Intel PIN, un vero e proprio framework di strumentazione binaria dinamica: potente, ma dotato di un SDK C++ proprietario da circa 100 MB e limitato a x86_64.\nAlla fine di quel post avevo promesso che saremmo andati oltre: automazione completa, qualsiasi binario, nessuna ricompilazione. Oggi manteniamo quella promessa con un approccio eBPF nativo, e il risultato è uno strumento chiamato funkoverage.\n(immagine per gentile concessione di https://www.pexels.com/@tanfeez/)\n🕳️ Perché l\u0026rsquo;eBPF cambia tutto eBPF è una tecnologia del kernel Linux che permette di eseguire piccoli programmi sandbox all\u0026rsquo;interno del kernel in risposta a eventi, senza caricare moduli del kernel o applicare patch al kernel stesso. Ai fini del tracciamento, ciò significa che possiamo agganciare i punti di ingresso delle funzioni con gli uprobes e ricevere notifiche nello spazio utente tramite un ring buffer, il tutto con un sovraccarico (overhead) trascurabile.\nDue funzionalità di eBPF rendono questo approccio particolarmente interessante per la misurazione della copertura:\nuprobe_multi (disponibile a partire da Linux 6.6) consente di collegare uprobes per un intero binario o libreria in una singola chiamata di sistema (syscall), passando tutti i nomi dei simboli e i \u0026ldquo;cookie\u0026rdquo; contemporaneamente. In precedenza era necessaria una syscall per ogni funzione; con 8.000 funzioni, si trattava di 8.000 syscall solo per la configurazione. Ora ne basta una.\nDeduplicazione della prima chiamata lato kernel: all\u0026rsquo;interno del programma BPF, utilizziamo un\u0026rsquo;operazione atomica compare-and-swap su un flag per ciascuna funzione, memorizzato in una mappa del kernel. Ciò significa che ogni funzione attiva esattamente un evento verso lo spazio utente, indipendentemente da quante volte viene chiamata durante il ciclo di vita del programma. Ai fini della copertura, questo è esattamente ciò che vogliamo: un segnale pulito sì/no senza rumore.\nEcco un confronto tra i diversi approcci:\nApproccio Overhead SDK richiesto Architettura Deduplicazione prima chiamata valgrind/callgrind ~10–20 volte più lento Nessuno x86_64 No Intel PIN ~5–10 volte più lento ~100MB C++ SDK x86_64 No eBPF uprobe_multi ~1–2% overhead Nessuno QUALSIASI Sì La differenza in termini di overhead è enorme nella pratica. Con valgrind, anche un banale gzip -h richiede mezzo secondo. Con gli uprobes, sono necessari pochi millisecondi: il programma gira a velocità essenzialmente nativa.\n🥷 Un impostore trasparente funkoverage è uno strumento scritto in Go puro che sfrutta questa infrastruttura eBPF per fornire una copertura a livello di funzione su qualsiasi binario ELF, senza bisogno del codice sorgente o di ricompilazione.\nIl design si basa su due binari che lavorano in sinergia:\nComponente Descrizione funkoverage Interfaccia a riga di comando (configurazione/installazione/report) funkoverage-shim Sostituto trasparente funkoverage è la CLI con cui si interagisce: installa e disinstalla lo shim, enumera le funzioni e genera i report di copertura.\nfunkoverage-shim è un \u0026ldquo;piccolo\u0026rdquo; binario Go che viene installato al posto del binario di destinazione. È completamente generico: non sa nulla di gzip o di qualsiasi altro programma. Quando viene richiamato, legge un file sidecar JSON per scoprire quali funzioni agganciare, attiva i probe BPF e quindi avvia in modo trasparente il vero binario.\nL\u0026rsquo;esecuzione di sudo funkoverage install /usr/bin/gzip esegue questi passaggi:\nSposta il vero binario gzip in /var/coverage/bin/gzip Enumera tutte le funzioni dalla tabella dei simboli (ricorrendo a DWARF se necessario) Scrive un file sidecar gzip.funcs.json con l\u0026rsquo;elenco dei simboli Copia il binario shim in /usr/bin/gzip Esegue setcap cap_bpf,cap_perfmon+ep sullo shim in modo che possa agganciare gli uprobes senza dover essere eseguito come root Da quel momento in poi, ogni invocazione di gzip passa in modo del tutto trasparente attraverso lo shim. La sequenza di runtime dello shim si presenta così:\nl\u0026#39;utente esegue \u0026#34;gzip -h\u0026#34; │ ▼ /usr/bin/gzip ← questo ora è lo shim │ ├── legge gzip.funcs.json ├── esegue il fork di un processo figlio (in pausa su una pipe) ├── carica il programma BPF incorporato ├── link.UprobeMulti(tutti i simboli) ← una sola syscall per immagine ├── inizializza la mappa del kernel \u0026#34;watched\u0026#34; con il PID del figlio ├── avvia la goroutine di lettura del ring buffer │ ├── sblocca il figlio tramite la pipe → il figlio esegue exec() del vero gzip │ ├── BPF si attiva alla prima chiamata di ciascuna funzione │ └── evento → ring buffer → demangling → _called.log │ └── il figlio termina → rimuove i probe → svuota il buffer → chiude il log Nessun LD_PRELOAD, nessun ptrace, nessuna modifica al binario. Il vero binario viene eseguito non modificato all\u0026rsquo;interno del processo figlio; lo shim genitore si limita a osservare ciò che accade a livello di kernel.\n🩺 Agganciare gzip, dal vivo Useremo di nuovo gzip — lo stesso bersaglio della parte 2 — in modo da poter confrontare direttamente i numeri.\nCompilate e installate funkoverage (avrete bisogno di Go 1.26+ e di un kernel Linux ≥ 6.6 con BTF abilitato):\n$ git clone https://github.com/ilmanzo/BinaryCoverage $ cd BinaryCoverage $ ./build.sh $ sudo cp funkoverage funkoverage-shim /usr/local/bin/ Ora installate lo shim su gzip:\n$ sudo funkoverage install /usr/bin/gzip ✓ moved /usr/bin/gzip → /var/coverage/bin/gzip ✓ enumerated 80 functions ✓ shim installed at /usr/bin/gzip (cap_bpf,cap_perfmon+ep) Eseguiamo il nostro semplice smoke test dalla parte 2:\n$ gzip -h Usage: gzip [OPTION]... [FILE]... Compress or uncompress FILEs (by default, compress FILES in-place). ... L\u0026rsquo;output è identico: gzip si comporta esattamente come prima. Ma ora abbiamo un file di log:\n$ tail -5 /var/coverage/data/gzip_*_called.log CALLED /var/coverage/bin/gzip main CALLED /var/coverage/bin/gzip try_help CALLED /var/coverage/bin/gzip license CALLED /var/coverage/bin/gzip rpl_printf CALLED /var/coverage/bin/gzip progerror Generiamo il report di copertura:\n$ funkoverage report /var/coverage/data /tmp/report $ cat /tmp/report/gzip.txt Functions: 9/80 (11.25%) 11.25% — esattamente quanto riportato da valgrind nella parte 2. Rassicurante! Ma questa volta gzip -h è stato eseguito in pochi millisecondi, non in mezzo secondo.\n🌒 A caccia delle funzioni oscure Lo shim accoda i dati al file di log a ogni esecuzione e il report si accumula. Seguiamo lo stesso percorso della parte 2 e osserviamo crescere la copertura.\nVerifichiamo la versione:\n$ gzip -V $ funkoverage report /var/coverage/data /tmp/report Functions: 10/80 (12.50%) Proviamo un percorso di errore, ad esempio un file inesistente:\n$ gzip foobar gzip: foobar: No such file or directory $ funkoverage report /var/coverage/data /tmp/report Functions: 19/80 (23.75%) C\u0026rsquo;è stato un bel balzo: il codice di gestione degli errori ha attivato funzioni che non avevamo ancora toccato. Ora proviamo ad effettuare una compressione reale:\n$ echo \u0026#34;hello funkoverage\u0026#34; \u0026gt; /tmp/test.txt $ gzip /tmp/test.txt $ gzip -d /tmp/test.txt.gz $ funkoverage report /var/coverage/data /tmp/report Functions: 52/80 (65.00%) 🎉 Stesso percorso di valgrind: 11% → 23% → 65%. Il report HTML mostra anche le funzioni non chiamate per nome, il che è comodissimo per sapere esattamente dove la suite di test presenti ancora delle lacune.\n🌍 Un solo binario, qualsiasi chip Quando abbiamo esteso funkoverage per supportare ARM64, non abbiamo dovuto modificare minimamente la logica del programma BPF: il set di istruzioni eBPF è indipendente dall\u0026rsquo;architettura. Ciò di cui avevamo bisogno era compilare il codice BPF in C per ciascuna architettura di destinazione e includere entrambi gli oggetti nella repository.\nLo strumento bpf2go del progetto cilium/ebpf genera un file Go per ciascuna architettura, e il meccanismo dei tag di compilazione di Go seleziona quello corretto in fase di build:\ntracer_x86_bpfel.go → //go:build 386 || amd64 tracer_arm64_bpfel.go → //go:build arm64 Gli oggetti pre-generati sono inclusi nella repository, quindi per una compilazione standard è necessario solo Go — non servono Clang o gli header del kernel. Su una macchina ARM64 (un Raspberry Pi, un\u0026rsquo;istanza cloud Graviton o una VM Apple Silicon), si applicano esattamente la stessa CLI e lo stesso flusso di lavoro.\n🏁 La copertura che vi spettava Abbiamo fatta molta strada dal semplice go build -cover del primo capitolo. Con eBPF e uprobe_multi ora disponiamo di uno strumento che:\nFunziona su qualsiasi binario ELF — strumenti di terze parti, pacchetti di distribuzione, demoni — senza bisogno del codice sorgente o di ricompilazione Introduce un overhead di runtime trascurabile, rendendolo pratico anche per le suite di test più lunghe Produce dati di copertura puliti, relativi solo alla prima chiamata, senza bisogno di script di collaudo o parsing manuale dei log Funziona sia su x86_64 che su ARM64 senza alcuna modifica al flusso di lavoro Se state scrivendo test di integrazione per un binario di cui non controllate i sorgenti, funkoverage vi offre finalmente quel ciclo di feedback sulla copertura che vi mancava.\nTrovate il progetto su github.com/ilmanzo/BinaryCoverage — segnalazioni di bug e pull request sono le benvenute.\nSe questo approccio vi incuriosisce, date un\u0026rsquo;occhiata anche a xcover, un altro strumento di copertura basato su eBPF. È stato recentemente presentato in un lightning talk al FOSDEM 2026 (slide).\nLasciate pure i vostri commenti e feedback, buon hacking! \u0026#x1f44b;\n","permalink":"https://ilmanzo.github.io/it/post/measuring-test-coverage-with-ebpf/","summary":"\u003ch2 id=\"-dove-eravamo-rimasti\"\u003e🧭 \u003ca href=\"https://www.youtube.com/watch?v=uB1D9wWxd2w\"\u003eDove eravamo rimasti\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eBenvenuti al nuovo capitolo della nostra serie dedicata alla misurazione della copertura dei test per i programmi binari!\u003c/p\u003e\n\u003cp\u003eNella \u003ca href=\"https://ilmanzo.github.io/it/post/measuring-coverage-of-integration-tests/\"\u003eparte 1\u003c/a\u003e abbiamo utilizzato il flag \u003ccode\u003e-cover\u003c/code\u003e integrato in Go: pulito e preciso, ma funziona solo se si possiede il codice sorgente e lo si può ricompilare. Nella \u003ca href=\"https://ilmanzo.github.io/it/post/measuring-test-coverage-on-binaries/\"\u003eparte 2\u003c/a\u003e abbiamo usato \u003ccode\u003evalgrind\u003c/code\u003e e \u003ccode\u003egdb\u003c/code\u003e per tracciare \u003ccode\u003egzip\u003c/code\u003e senza toccarne il sorgente. Nella \u003ca href=\"https://ilmanzo.github.io/it/post/pintool-function-tracing/\"\u003eparte 3\u003c/a\u003e abbiamo esplorato Intel PIN, un vero e proprio framework di strumentazione binaria dinamica: potente, ma dotato di un SDK C++ proprietario da circa 100 MB e limitato a x86_64.\u003c/p\u003e","title":"Quanto codice stai testando? (4)"},{"content":"📦 Quel profumo di pacchetto nuovo Ci siamo passati tutti: vedi una fantastica nuova applicazione su GitHub e vuoi \u0026ldquo;scartarla\u0026rdquo; immediatamente. Ma nel tradizionale mondo Linux, aprire un pacchetto spesso assomiglia ad aprire una scatola di brillantini in salotto: prima ancora di rendersene conto, le dipendenze sono sparse ovunque, e tre mesi dopo si trovano ancora strane versioni di librerie in /usr/lib.\nEcco perché ho iniziato a usare Flatpak. È come un\u0026rsquo;esperienza di unboxing in cui la scatola rimane una scatola. Ottieni tutte le novità all\u0026rsquo;interno, ma il disordine rimane confinato. Vediamo cosa succede quando strappiamo la pellicola protettiva.\nCrediti immagine: Boris Hamer\n🐤 Bubblewrap + OSTree: i vostri sigilli di garanzia Quando si apre un Flatpak, ci sono due livelli di \u0026ldquo;confezionamento\u0026rdquo; che mantengono tutto al sicuro:\nBubblewrap: Pensatelo come il nastro adesivo trasparente di sicurezza attorno ai componenti interni. Utilizza i namespace di Linux per assicurarsi che l\u0026rsquo;app veda solo i propri file, senza poter \u0026ldquo;sconfinare\u0026rdquo; nel sistema operativo host. È una sandbox che tiene le mani appiccicose dell\u0026rsquo;app lontane dai file di sistema. OSTree: È il modo in cui sono conservati i \u0026ldquo;pezzi\u0026rdquo;. È come un sistema di scaffali modulari. Se dieci pacchetti diversi hanno bisogno dello stesso \u0026ldquo;cavo di alimentazione\u0026rdquo; (runtime), OSTree fa in modo che ci sia un solo cavo fisico sullo scaffale. Deduplicazione: perché a nessuno servono dieci copie del runtime GNOME. 👣 Uno sguardo attraverso la confezione Una delle parti migliori di un \u0026ldquo;Pack\u0026rdquo; è che viene fornito con un manifesto (manifest). Non c\u0026rsquo;è bisogno di indovinare cosa ci sia dentro o cosa stia cercando di fare. Potete sbirciare attraverso la plastica prima ancora di avviarlo.\nDiamo un\u0026rsquo;occhiata al \u0026ldquo;Manuale di istruzioni\u0026rdquo; di Obsidian:\n$ flatpak info --show-metadata md.obsidian.Obsidian Sotto l\u0026rsquo;intestazione [Context], vedrete esattamente cosa questa applicazione ha il permesso di toccare. Se richiede network e pulseaudio, sapete già che comunicherà con il web ed emetterà suoni. È la massima trasparenza del tipo \u0026ldquo;Cosa c\u0026rsquo;è nella scatola?\u0026rdquo;.\n🔧 Gestire i pacchetti: la cassetta degli attrezzi CLI Non serve un\u0026rsquo;interfaccia grafica sofisticata per gestire i vostri pacchetti. La riga di comando è più veloce e offre un controllo maggiore sull\u0026rsquo;\u0026ldquo;unboxing\u0026rdquo;.\nLe operazioni quotidiane Attività Comando Cerca nel catalogo flatpak search \u0026lt;nome\u0026gt; Installa un nuovo pacchetto flatpak install flathub \u0026lt;app_id\u0026gt; Mostra lo scaffale flatpak list --app Aggiorna i pacchetti flatpak update Buttare via gli scarti A volte capita di \u0026ldquo;aprire\u0026rdquo; alcune app e decidere che non piacciono. Se le avete disinstallate lasciando dietro di sé il \u0026ldquo;confezionamento\u0026rdquo; in eccesso (i runtime), eseguite questo comando per ripulire il tutto:\n$ flatpak uninstall --unused 🛠️ Fai da te: costruire la propria scatola Vi siete mai chiesti quanto sia difficile inserire un proprio script in una \u0026ldquo;scatola\u0026rdquo;? È sorprendentemente semplice. Tutto ciò di cui avete bisogno è un manifesto (il progetto) e il vostro codice.\nCreiamo un\u0026rsquo;app \u0026ldquo;Hello Flatpak\u0026rdquo;. Innanzitutto, create uno script chiamato hello.sh:\n#!/bin/sh echo \u0026#34;Hello from inside the box! I can\u0026#39;t see your secrets!\u0026#34; Ora, create un file manifesto chiamato org.test.Hello.yaml:\napp-id: org.test.Hello runtime: org.freedesktop.Platform runtime-version: \u0026#39;23.08\u0026#39; sdk: org.freedesktop.Sdk command: hello.sh modules: - name: hello buildsystem: simple build-commands: - install -D hello.sh /app/bin/hello.sh sources: - type: file path: hello.sh Per compilarlo e \u0026ldquo;inscatolarlo\u0026rdquo;, avrete bisogno di flatpak-builder. Eseguite questi due comandi:\n# Compila l\u0026#39;applicazione in una cartella chiamata \u0026#39;build-dir\u0026#39; $ flatpak-builder --user --install --force-clean build-dir org.test.Hello.yaml # Esegui la tua nuova creazione $ flatpak run org.test.Hello In questo modo avete creato un\u0026rsquo;applicazione in modalità sandbox. Ha il proprio prefisso /app e non può toccare la vostra directory home a meno che non aggiungiate esplicitamente una sezione finish-args al manifesto.\n🎨 Personalizzare la scatola a proprio piacimento Una delle cose migliori di Flatpak è che il \u0026ldquo;Manuale di istruzioni\u0026rdquo; non è scolpito nella pietra. Se non vi piace un permesso scelto dallo sviluppatore, potete semplicemente sovrascriverlo.\nLa via della CLI Il comando flatpak override è il vostro migliore alleato in questo caso. Vi consente di \u0026ldquo;riconfezionare\u0026rdquo; un\u0026rsquo;app al volo.\nTagliare i ponti: Non vi fidate di un\u0026rsquo;app? Bloccate il suo accesso a internet: $ flatpak override --nosocket=network org.some.App Accesso mirato alle cartelle: Avete bisogno che il vostro editor veda un SSD esterno? $ flatpak override --filesystem=/media/external_drive org.some.IDE Iniezione di variabili d\u0026rsquo;ambiente: Volete forzare un tema specifico o una modalità di debug? $ flatpak override --env=DEBUG=1 org.some.App Il pulsante \u0026ldquo;annulla\u0026rdquo; Se esagerate e l\u0026rsquo;app smette di funzionare, niente panico. Potete sempre ripristinare le impostazioni di fabbrica con un unico comando:\n$ flatpak override --reset org.some.App Consiglio utile: Flatseal Se preferite una dashboard visiva per gestire questi interruttori, provate Flatseal. È esso stesso un Flatpak che offre un\u0026rsquo;interfaccia pulita per gestire i permessi di ogni app presente sul sistema. È lo strumento di ispezione di sicurezza definitivo.\n🎁 Contenuti extra per smanettoni curiosi Visto che stiamo esplorando la confezione a fondo, ecco tre cose extra che potete fare con i vostri Flatpak e che forse non conoscete:\n1. Il pacchetto portatile \u0026ldquo;offline\u0026rdquo; Avete mai desiderato passare un\u0026rsquo;app specifica a un amico senza connessione a internet, o installarla su un server isolato? Potete esportare un\u0026rsquo;app installata in un singolo file .flatpak:\n$ flatpak create-bundle /path/to/repo my-app.flatpak org.some.App In questo modo avrete un programma di installazione portatile e autonomo!\n2. Anche le app CLI possono essere pacchetti! I Flatpak non sono solo per pesanti interfacce grafiche come GIMP o Obsidian. Su Flathub potete trovare anche strumenti CLI ad alte prestazioni come neovim, ffmpeg o btop. Per eseguirli come se fossero nativi, basta aggiungere un alias al vostro .bashrc:\nalias nvim=\u0026#39;flatpak run io.neovim.nvim\u0026#39; 3. XDG Portal: un ponte cortese verso i file dell\u0026rsquo;host Vi siete mai chiesti come faccia un\u0026rsquo;applicazione isolata ad aprire un file senza avere il permesso di vedere l\u0026rsquo;intero disco? Il merito è del servizio XDG Portal.\nQuando si fa clic su \u0026ldquo;Apri\u0026rdquo;, l\u0026rsquo;app chiede al servizio Portal (che risiede sull\u0026rsquo;host) di mostrare la finestra di selezione dei file. Voi scegliete il file e il Portal passa un \u0026ldquo;token\u0026rdquo; temporaneo all\u0026rsquo;app, valido esclusivamente per quel singolo file. È come la tessera magnetica di un hotel: vi permette di aprire la vostra stanza, ma non vi dà le chiavi di tutto l\u0026rsquo;edificio.\n🕰️ Repository, cronologia e piccole macchine del tempo Flatpak non è legato a un unico \u0026ldquo;Store\u0026rdquo;. Utilizza i Remotes, che sono semplicemente repository in cui sono conservati i pacchetti.\nAggiungere le sorgenti La maggior parte delle persone si limita a usare Flathub, ma è possibile averne quanti se ne desidera (repo Beta, GNOME nightly, ecc.):\n$ flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo Viaggio nel tempo tra le versioni (rollback) Questa è una funzionalità straordinaria. Poiché Flatpak utilizza OSTree, mantiene una cronologia dei vostri aggiornamenti. Se una nuova versione di un\u0026rsquo;app interrompe il vostro flusso di lavoro, potete letteralmente viaggiare indietro nel tempo.\nMostra la cronologia: $ flatpak remote-info --log flathub org.some.App Ripristina un commit specifico: $ flatpak update --commit=abcdef12345 org.some.App Non dovrete più attendere che lo sviluppatore risolva il bug: vi basterà tornare alla versione precedente che funzionava correttamente.\n🏁 Conclusioni Flatpak è in grado di trasformare qualsiasi workstation da officina disordinata a scaffalatura pulita e modulare. Potete aprire, testare e scartare \u0026ldquo;pacchetti\u0026rdquo; di software senza preoccuparvi che un file .so vagante possa rovinarvi la giornata.\nLa prossima volta che state per lanciare sudo apt install per installare una pesante suite di strumenti, provate a usare un Flatpak. Tenete il disordine dentro la scatola. Buon Hacking!\n","permalink":"https://ilmanzo.github.io/it/post/flatpak-unboxing-guide/","summary":"\u003ch2 id=\"-quel-profumo-di-pacchetto-nuovo\"\u003e📦 Quel profumo di \u003cem\u003epacchetto nuovo\u003c/em\u003e\u003c/h2\u003e\n\u003cp\u003eCi siamo passati tutti: vedi una fantastica nuova applicazione su GitHub e vuoi \u0026ldquo;scartarla\u0026rdquo; immediatamente. Ma nel tradizionale mondo Linux, aprire un pacchetto spesso assomiglia ad aprire una scatola di brillantini in salotto: prima ancora di rendersene conto, le dipendenze sono sparse ovunque, e tre mesi dopo si trovano ancora strane versioni di librerie in \u003ccode\u003e/usr/lib\u003c/code\u003e.\u003c/p\u003e\n\u003cp\u003eEcco perché ho iniziato a usare \u003cstrong\u003eFlatpak\u003c/strong\u003e. È come un\u0026rsquo;esperienza di unboxing in cui la scatola rimane una scatola. Ottieni tutte le novità all\u0026rsquo;interno, ma il disordine rimane confinato. Vediamo cosa succede quando strappiamo la pellicola protettiva.\u003c/p\u003e","title":"Flatpak: l'unboxing della sandbox"},{"content":"👋 Intro Se hai mai passato del tempo a mettere in sicurezza le applicazioni Linux, probabilmente conosci la frustrazione del modello di permessi tutto o niente. Nel tipico ambiente Linux, una volta che un processo viene avviato, solitamente ha molto più accesso al filesystem di quanto ne abbia effettivamente bisogno. Sebbene disponiamo di strumenti come seccomp, chroot o moduli pesanti come SELinux e AppArmor, questi spesso sembrano troppo complessi per un semplice sandboxing a livello applicativo.\nLandlock cambia questo scenario. Dal suo merge nel kernel Linux nella versione 5.13, è diventato una svolta per gli sviluppatori. Consente a un processo di limitarsi autonomamente senza richiedere privilegi di root, spostando la sicurezza dalle policy di sistema globali direttamente all\u0026rsquo;interno del codice della tua applicazione.\n⏳ L\u0026rsquo;evoluzione di Landlock Landlock è un\u0026rsquo;API in evoluzione che è cresciuta in modo significativo. Il kernel utilizza le versioni ABI per segnalare quali funzionalità sono disponibili su uno specifico sistema. Questo versionamento è cruciale perché permette alla tua sandbox di degradare elegantemente sui kernel più vecchi, offrendo comunque la massima sicurezza possibile su quelli moderni.\nIl viaggio è iniziato con la versione ABI v1 nel Kernel 5.13, che si concentrava sui diritti di base del filesystem come la lettura e la scrittura. Con la maturazione del progetto, la versione 2 ha aggiunto il supporto per il reparenting dei file e la versione 3 ha introdotto il controllo esplicito sulla troncabilità dei file. Più di recente, la versione 4 ha introdotto il supporto di rete TCP, seguito dal controllo ioctl nella versione 5 e dal controllo dell\u0026rsquo;ambito IPC (IPC scoping) nella versione 6. L\u0026rsquo;ultimo traguardo, la versione 8, ha introdotto TSYNC, che consente un\u0026rsquo;applicazione della sicurezza atomica (atomic security enforcement) su tutti i thread di un processo.\nPer un elenco completo e aggiornato di queste funzionalità, puoi sempre consultare la Documentazione ufficiale dell\u0026rsquo;API Landlock per lo Userspace o visitare il sito web del progetto su landlock.io.\n🏗️ Capire l\u0026rsquo;architettura A differenza dei tradizionali moduli di sicurezza gestiti dagli amministratori di sistema, Landlock è progettato per gli sviluppatori di applicazioni. È completamente non privilegiato, il che significa che qualsiasi processo può avviare una sandbox senza richiedere sudo o funzionalità (capabilities) speciali.\nIl sistema è inoltre impilabile (stackable). Puoi applicare più livelli di regole, in cui ogni nuovo set di regole limita ulteriormente il processo. Una volta applicata una restrizione, questa non può essere rimossa o allentata, e ogni processo figlio generato dall\u0026rsquo;applicazione nasce automaticamente all\u0026rsquo;interno della stessa sandbox. Aspetto fondamentale, Landlock è basato sugli oggetti (object-based). Limita l\u0026rsquo;accesso in base alla rappresentazione interna del kernel di un file, il suo inode, anziché semplicemente sul suo nome. Questo lo rende intrinsecamente immune a trucchi comuni come gli attacchi di symlink o il path traversal.\nIl flusso operativo segue un semplice schema a tre fasi: prima si definisce un set di regole per le operazioni gestite, poi si associano (bind) percorsi specifici del filesystem o porte di rete a tali permessi e infine si applica il set di regole (commit) al processo corrente.\n⚙️ L\u0026rsquo;interfaccia del kernel Sotto il cofano, Landlock è gestito attraverso tre syscall principali. Innanzitutto, landlock_create_ruleset inizializza un nuovo set di regole di sicurezza in cui si specificano le operazioni che si desidera gestire. Qualsiasi operazione non specificata rimane non limitata.\nSuccessivamente, si usa landlock_add_rule per concedere permessi specifici a directory o porte. Attualmente, questa operazione utilizza principalmente il tipo LANDLOCK_RULE_PATH_BENEATH per concedere l\u0026rsquo;accesso a uno specifico albero di directory. Infine, landlock_restrict_self applica il set di regole al processo corrente. Prima di questa chiamata, è necessario assicurarsi che PR_SET_NO_NEW_PRIVS sia impostato tramite prctl per impedire al processo di acquisire privilegi che potrebbero aggirare la sandbox.\n🛡️ Mitigazione pratica degli attacchi Per comprendere il valore di questo approccio, considera un classico attacco di path traversal in cui un utente malintenzionato tenta di leggere /etc/shadow usando sequenze di ../. Poiché Landlock applica la sicurezza a livello di inode del kernel, questi trucchi basati sui nomi semplicemente falliscono. Se il file non è presente nel set di regole, il kernel restituisce un errore di Permission Denied nell\u0026rsquo;istante esatto in cui il file viene aperto.\nQuesta protezione si estende anche alla rete e all\u0026rsquo;IPC. Con il controllo dell\u0026rsquo;accesso alla rete, a un processo compromesso può essere impedito di connettersi a server esterni di comando e controllo (C2). Abilitando il controllo dell\u0026rsquo;ambito IPC (IPC scoping), è possibile impedire a un processo di inviare segnali come SIGKILL a qualsiasi PID che non faccia parte del proprio dominio di sicurezza limitato.\nOltre a questi esempi di base, Landlock fornisce una difesa robusta contro diversi altri vettori di attacco comuni:\nRansomware e cifratura di massa dei file: Limitando rigorosamente l\u0026rsquo;accesso in scrittura solo alle directory necessarie (como una cartella temporanea o una specifica directory di dati) e lasciando il resto del filesystem in sola lettura o inaccessibile, ai ransomware viene strutturalmente impedito di modificare o cifrare i file dell\u0026rsquo;utente. Esempio: Un lettore PDF all\u0026rsquo;interno di una sandbox Landlock ha solo accesso in lettura a /home/user/Documents e nessun accesso in scrittura altrove. Se il lettore viene compromesso da un PDF malevolo contenente un ransomware, questo semplicemente non potrà cifrare i tuoi file. Attacchi alla Supply Chain: Le applicazioni moderne si affidano fortemente a dipendenze di terze parti. Se un aggiornamento malevolo in una libreria tenta di raccogliere le chiavi SSH o stabilire connessioni di rete in uscita non autorizzate, Landlock bloccherà l\u0026rsquo;operazione perché la sandbox dell\u0026rsquo;applicazione vieta esplicitamente tali azioni. Esempio: Uno script di compilazione limitato da Landlock a leggere solo ./src e scrivere solo in ./dist. Se un pacchetto compromesso tenta di leggere ~/.ssh/id_rsa e aprire una connessione di rete per inviarlo a un utente malintenzionato, Landlock bloccherà entrambe le azioni. Esfiltrazione di dati: Limitando l\u0026rsquo;accesso in lettura a posizioni sensibili (come ~/.ssh, ~/.aws o /etc/shadow) e bloccando l\u0026rsquo;accesso alla rete, gli aggressori che ottengono l\u0026rsquo;esecuzione di codice arbitrario non saranno in grado di rubare e trasmettere dati sensibili. Esempio: Un server web ha bisogno di accedere solo a /var/www/html. Se un aggressore sfrutta una vulnerabilità di Local File Inclusion (LFI) per cercare di leggere /etc/passwd o /etc/shadow, il kernel negherà la lettura. Escalation dei privilegi: Poiché i set di regole di Landlock vengono ereditati da tutti i processi figli e richiedono il flag PR_SET_NO_NEW_PRIVS, un aggressore non può aggirare la sandbox eseguendo eseguibili SUID. Il processo figlio rimane limitato dalle stesse identiche regole del genitore. Esempio: Anche se un aggressore trova il modo di eseguire sudo o un altro eseguibile SUID root dall\u0026rsquo;interno della sandbox, il flag PR_SET_NO_NEW_PRIVS garantisce che il processo non elevi effettivamente i propri privilegi, rendendo l\u0026rsquo;exploit inutile. Divulgazione di informazioni e manipolazione del sistema: I filesystem virtuali come /proc e /sys contengono una miniera di informazioni sensibili, inclusi indirizzi del kernel, configurazioni hardware e variabili d\u0026rsquo;ambiente di altri processi. Inoltre espongono endpoint scrivibili che possono modificare i parametri del kernel. Per impostazione predefinita, Landlock limita l\u0026rsquo;accesso a questi endpoint globali a meno che non sia esplicitamente consentito. Esempio: Un aggressore che sfrutta un bug in un\u0026rsquo;applicazione server potrebbe tentare di leggere /proc/kallsyms per aggirare la Kernel Address Space Layout Randomization (KASLR) o leggere /proc/self/environ per rubare chiavi API. Se il set di regole Landlock dell\u0026rsquo;applicazione non concede esplicitamente l\u0026rsquo;accesso a /proc, questi tentativi vengono immediatamente bloccati. 👑 Rendere Landlock idiomatico in Nim Il nostro wrapper Nim bilancia questo controllo a basso livello con la sicurezza ad alto livello. Utilizziamo enum type-safe come FsAccess, NetAccess e Scope invece di maschere di bit grezze. Il cuore della libreria è la procedura restrictTo, che gestisce l\u0026rsquo;intero ciclo di vita mascherando automaticamente i flag che il kernel corrente non supporta.\nUtilizzando la metaprogrammazione di Nim, possiamo fare un passo ulteriore. La macro toStaticLandlock calcola le maschere di bit del kernel a tempo di compilazione, sostituendo i cicli a runtime con interi letterali. Forniamo anche una DSL dichiarativa sandbox: che trasforma un blocco di codice leggibile in una sequenza complessa di inizializzazione.\nAspetto cruciale, restrictTo (e la macro sandbox:) restituisce un oggetto capability Sandboxed in caso di successo. Questo segue il Witness Pattern: richiedendo questo oggetto come argomento nelle tue procedure sensibili, crei una garanzia a tempo di compilazione che tali procedure possano essere eseguite solo dopo che la sandbox sia stata inizializzata correttamente.\n💻 Esempio di implementazione Ecco come appaiono tutti questi elementi in un\u0026rsquo;applicazione reale. Nota come processFile richieda il testimone (witness) Sandboxed, garantendo che non possa essere chiamato accidentalmente prima che le restrizioni vengano applicate.\nimport landlock, os # This function REQUIRES proof of sandboxing proc processFile(proof: Sandboxed, path: string) = # \u0026#39;proof\u0026#39; has no methods - it just proves we\u0026#39;re sandboxed writeFile(path, \u0026#34;Data processed securely\u0026#34;) let workDir = \u0026#34;/tmp/my_sandbox\u0026#34; if not dirExists(workDir): createDir(workDir) try: # \u0026#39;sb\u0026#39; is just proof - no fields, no methods to call let sb = sandbox: allow workDir, {ReadFile, WriteFile, MakeReg} # The value of \u0026#39;sb\u0026#39; is that it EXISTS - proving sandbox is active processFile(sb, workDir / \u0026#34;safe.txt\u0026#34;) # Compiles - we have proof # This won\u0026#39;t compile - no proof available: # processFile(???, \u0026#34;file.txt\u0026#34;) # Error: missing Sandboxed argument echo \u0026#34;Sandboxed successfully!\u0026#34; except LandlockError as e: echo \u0026#34;Failed: \u0026#34;, e.msg Il tipo Sandboxed è intenzionalmente vuoto: si tratta di un pattern di sicurezza basato sulle capability. Il compilatore garantisce che il codice critico dal punto di vista della sicurezza possa essere eseguito solo se si possiede il token di prova.\n🏁 Conclusione Landlock e Nim rappresentano una combinazione potente per la creazione di sistemi sicuri. Sfruttando la metaprogrammazione, possiamo trasformare una complessa API del kernel in una garanzia statica applicata sia dal compilatore che dal kernel. È un modo pragmatico per implementare il principle of least privilege (principio del minimo privilegio) senza sacrificare la produttività dello sviluppatore.\nIl codice completo del wrapper Nim e il Proof of Concept sono disponibili su GitHub all\u0026rsquo;indirizzo https://github.com/ilmanzo/landlock-nim-poc.\nStay secure, stay pragmatic.\n","permalink":"https://ilmanzo.github.io/it/post/landlock_idiomatic_sandboxing_in_nim/","summary":"\u003ch1 id=\"-intro\"\u003e👋 Intro\u003c/h1\u003e\n\u003cp\u003eSe hai mai passato del tempo a mettere in sicurezza le applicazioni Linux, probabilmente conosci la frustrazione del modello di permessi \u003cem\u003etutto o niente\u003c/em\u003e. Nel tipico ambiente Linux, una volta che un processo viene avviato, solitamente ha \u003cem\u003emolto più\u003c/em\u003e accesso al filesystem di quanto ne abbia effettivamente bisogno. Sebbene disponiamo di strumenti come \u003ccode\u003eseccomp\u003c/code\u003e, \u003ccode\u003echroot\u003c/code\u003e o moduli pesanti come \u003cstrong\u003eSELinux\u003c/strong\u003e e \u003cstrong\u003eAppArmor\u003c/strong\u003e, questi spesso sembrano troppo complessi per un semplice sandboxing a livello applicativo.\u003c/p\u003e","title":"Sandboxing idiomatico con Landlock in Nim"},{"content":"🧂 Salt senza sudo In continuità con il post precedente, oggi metteremo all\u0026rsquo;opera i nostri container gestiti da systemd e li utilizzeremo per alcune attività utili.\nL\u0026rsquo;idea è quella di configurare un ambiente per imparare come funziona il configuration management Salt e sperimentare/smanettare con esso, senza nemmeno aver bisogno dei permessi di root o di sudo. Dopotutto, nel mondo delle infrastrutture, il Salt deve scorrere!\nConfigureremo quindi due container, uno come salt master e l\u0026rsquo;altro come salt \u0026ldquo;minion\u0026rdquo; (che rappresenta la macchina che verrà configurata tramite salt).\n+-------------+ +-------------+ | | Pub (4505) | | | salt-master | --------------\u0026gt; | salt-minion | | | Ret (4506) | | | | \u0026lt;-------------- | | +-------------+ +-------------+ 🍳 Iniziamo a cucinare Per il mio esperimento ho usato openSUSE Tumbleweed, in quanto mi permette di testare le ultime versioni dei miei pacchetti preferiti.\n# Install prerequisites $ sudo zypper install podman systemd-container # For rootless containers to start on boot without the user being logged in $ loginctl enable-linger $USER # This directory will contain our containers (pun intended) $ mkdir -p ~/.config/containers/systemd/ # Create the directory structure for our Salt lab $ mkdir -p ~/salt_lab/config ~/salt_lab/srv/salt ~/salt_lab/srv/pillar # Create the initial configuration files (so Podman mounts files, not directories) $ touch ~/salt_lab/config/master_custom.conf $ touch ~/salt_lab/config/minion.conf Ora prepariamo i file Quadlet per i nostri due container, proprio come abbiamo fatto l\u0026rsquo;ultima volta:\n# ~/.config/containers/systemd/salt-master.container [Unit] Description=Salt Master Lab [Container] Image=registry.opensuse.org/opensuse/leap:15.6 ContainerName=salt-master HostName=salt-master Network=saltnet Volume=%h/salt_lab/srv/salt:/mnt/salt:Z Volume=%h/salt_lab/srv/pillar:/mnt/pillar:Z Volume=%h/salt_lab/config/master_custom.conf:/etc/salt/master.d/master_custom.conf:Z # Persist our Master\u0026#39;s keys across container restarts Volume=%h/salt_lab/config/pki/master:/etc/salt/pki:Z Exec=bash -c \u0026#34;zypper --non-interactive install salt-master \u0026amp;\u0026amp; salt-master -l debug\u0026#34; [Service] Restart=always [Install] WantedBy=default.target # ~/.config/containers/systemd/salt-minion.container [Unit] Description=Salt Minion Lab After=salt-master.service [Container] Image=registry.opensuse.org/opensuse/leap:15.6 ContainerName=salt-minion HostName=salt-minion Network=saltnet Volume=%h/salt_lab/config/minion.conf:/etc/salt/minion.d/minion.conf:Z # Persist our Minion\u0026#39;s keys across container restarts Volume=%h/salt_lab/config/pki/minion:/etc/salt/pki:Z Exec=bash -c \u0026#34;zypper --non-interactive install salt-minion \u0026amp;\u0026amp; salt-minion -l debug\u0026#34; [Service] Restart=always [Install] WantedBy=default.target 🛡️ A cosa serve il flag :Z ? Podman rootless è uno strumento orientato innanzitutto alla sicurezza.\nQuando monti un volume dal tuo host all\u0026rsquo;interno di un container, i moduli di sicurezza di Linux (come SELinux) impediscono di default al container di toccare quei file. Il flag :Z indica a Podman:\n\u0026ldquo;Ehi, sto montando questa cartella. Per favore, ricollegale l\u0026rsquo;etichetta (relabel) in modo che questo specifico container (e solo questo) abbia i permessi privati per leggere e scrivere qui.\u0026rdquo;\nSenza :Z, il tuo Salt Master vedrà le cartelle ma riceverà un errore di Permission Denied quando proverà a leggere i tuoi file .sls, perché il livello di sicurezza dell\u0026rsquo;host non riconosce l\u0026rsquo;utente root \u0026ldquo;interno\u0026rdquo; del container come proprietario valido.\nPer rendere persistente il nostro lavoro, il container monterà alcuni volumi dal sistema host, quindi prepariamo le directory e il loro contenuto.\nAbbiamo già creato le directory nella fase di configurazione, quindi ora possiamo popolare i file di configurazione:\n# The Master Config (`~/salt_lab/config/master_custom.conf`) interface: 0.0.0.0 file_roots: base: - /mnt/salt pillar_roots: base: - /mnt/pillar # The Minion Config (`~/salt_lab/config/minion.conf`) master: salt-master C\u0026rsquo;è un altro concetto nuovo qui: i container dovranno comunicare tra loro, quindi dobbiamo creare una \u0026ldquo;rete\u0026rdquo; personalizzata per loro:\npodman network create saltnet In una configurazione standard, i container sono spesso isolati o si affidano a indirizzi IP imprevedibili. Creando la rete personalizzata saltnet, abilitiamo il Service Discovery tramite DNS interno. Quando il Salt Minion tenta di connettersi all\u0026rsquo;host salt-master, non ha bisogno di conoscere un indirizzo IP; interroga semplicemente il risolutore DNS integrato nella rete di Podman. Podman intercetta questa richiesta e mappa il nome salt-master sull\u0026rsquo;IP interno corretto del container (ad esempio, 10.89.0.2). Ciò crea un ambiente stabile e \u0026ldquo;plug-and-play\u0026rdquo; in cui la nostra infrastruttura può trovare automaticamente il proprio \u0026ldquo;cervello\u0026rdquo; (il Master), anche se i container vengono riavviati o se vengono loro assegnati nuovi IP dietro le quinte.\nSenza una rete personalizzata, Podman utilizza di default un bridge di base che non fornisce la risoluzione dei nomi. Utilizzando saltnet, evitiamo di cablare in modo rigido indirizzi IP fragili per orientarci verso un\u0026rsquo;infrastruttura dichiarativa in cui i servizi si trovano l\u0026rsquo;un l\u0026rsquo;altro in base alla propria identità.\nOra puoi finalmente avviare i container/servizi:\n$ systemctl --user start salt-master salt-minion (al primissimo avvio, potrebbe essere necessario un po\u0026rsquo; di tempo per scaricare le immagini e installare i pacchetti salt)\n📖 Impariamo il gergo Per un principiante, Salt può sembrare l\u0026rsquo;inventario di una cucina. Ecco la spiegazione dei termini principali:\nTermine Definizione Analogo a\u0026hellip; Master Il server centrale che memorizza le configurazioni e impartisce i comandi. Il direttore d\u0026rsquo;orchestra Minion L\u0026rsquo;agente in esecuzione sul server di destinazione che esegue gli ordini del Master. I musicisti State (.sls) Un file YAML che descrive lo stato finale desiderato di un sistema (es. \u0026ldquo;Questo pacchetto deve essere installato\u0026rdquo;). Lo spartito musicale Pillar Dati sicuri e privati (come le password) definiti sul Master e inviati solo a Minion specifici. La cassaforte segreta Grains \u0026ldquo;Dati di fatto\u0026rdquo; statici su un Minion (versione del sistema operativo, CPU, RAM) che questo segnala al Master. La carta d\u0026rsquo;identità JID ID del Job. Ogni comando inviato dal Master riceve un ID univoco provvisto di timestamp. La ricevuta 📡 Mi sentite adesso? $ podman exec -it salt-minion ping -c3 salt-master PING salt-master.dns.podman (10.89.0.6) 56(84) bytes of data. 64 bytes from salt-master (10.89.0.6): icmp_seq=1 ttl=64 time=0.014 ms 64 bytes from salt-master (10.89.0.6): icmp_seq=2 ttl=64 time=0.037 ms 64 bytes from salt-master (10.89.0.6): icmp_seq=3 ttl=64 time=0.034 ms 64 bytes from ... Accedi al master e controlla le chiavi in attesa:\n$ podman exec -it salt-master bash salt-master:/ # salt-key -L Accepted Keys: Denied Keys: Unaccepted Keys: salt-minion \u0026lt;--- THIS IS THE MINION! Rejected Keys: Dobbiamo accettare la chiave!\nsalt-master:/ # salt-key -a salt-minion -y The following keys are going to be accepted: Unaccepted Keys: salt-minion Key for minion salt-minion accepted. Ora assicuriamoci che il master possa controllare il minion:\nsalt-master:/ # salt \u0026#39;salt-minion\u0026#39; test.ping salt-minion: True 🤝 La stretta di mano segreta Quando avvii un Minion nuovo di zecca, questo non si fida ciecamente del Master, e il Master non si fida assolutamente del Minion. Ecco come si svolge lo scambio passo dopo passo:\nNon appena il servizio salt-minion viene avviato per la prima volta, genera la propria coppia di chiavi RSA (una chiave pubblica e una privata) localmente in /etc/salt/pki/minion/.\nIl Minion invia la propria chiave pubblica via rete al Master. In pratica dice: \u0026ldquo;Ciao, sono salt-minion. Ecco la mia chiave pubblica. Vorrei entrare a far parte della tua infrastruttura.\u0026rdquo;\nIl Master riceve la chiave e la colloca in una \u0026ldquo;sala d\u0026rsquo;attesa\u0026rdquo; (la directory /etc/salt/pki/master/minions_pre/).\nIn questa fase, se esegui salt-key -L, vedrai il minion in Rosso (Unaccepted).\nIl Master non invierà ancora alcun comando a questo minion.\nQuando esegui salt-key -a salt-minion, il Master sposta quella chiave pubblica nella cartella \u0026ldquo;Accepted\u0026rdquo; (/etc/salt/pki/master/minions/).\nIl Master invia quindi la propria chiave pubblica al Minion.\nOra entrambi possiedono la chiave pubblica dell\u0026rsquo;altro. Possono usarle per negoziare una chiave di sessione AES temporanea per comunicazioni cifrate e velocissime.\nSe un hacker tentasse di impersonare il tuo salt-minion assegnando al proprio laptop lo stesso nome e provando a unirsi alla tua rete:\nIl Master vedrebbe una nuova chiave pubblica associata a un nome già esistente.\nSalt mostrerebbe un avviso di sicurezza enorme: \u0026ldquo;Attenzione! La chiave di salt-minion è cambiata! Potrebbe trattarsi di un attacco Man-in-the-Middle!\u0026rdquo;\nIl Master rifiuterà di comunicare con il \u0026ldquo;nuovo\u0026rdquo; minion finché non avrai cancellato manualmente la vecchia chiave e accettato quella nuova.\n🆔 Non perdere la tua identità In una configurazione standard dei container, queste chiavi risiedono all\u0026rsquo;interno del filesystem virtuale del container. Se elimini il container senza un volume persistente per /etc/salt/pki, dovrai accettare nuovamente le chiavi a ogni riavvio del laboratorio!\nNella nostra definizione Quadlet, abbiamo risolto questo problema montando una directory locale dall\u0026rsquo;host (~/salt_lab/config/pki/) all\u0026rsquo;interno del container.\nUna guida rapida (Cheat Sheet) per la gestione delle chiavi:\nComando Azione salt-key -L Elenca tutte le chiavi (Accepted, Unaccepted, Rejected). salt-key -a \u0026lt;nome\u0026gt; Accetta la chiave di un minion specifico. salt-key -A Accetta tutte le chiavi in attesa (usare con cautela!). salt-key -d \u0026lt;nome\u0026gt; Elimina una chiave (in pratica, \u0026ldquo;licenzia\u0026rdquo; il minion). salt-key -f \u0026lt;nome\u0026gt; Fingerprint - Mostra la \u0026ldquo;carta d\u0026rsquo;identità\u0026rdquo; di una chiave. Fingerprint (Impronta digitale): Una breve stringa di lettere e numeri che rappresenta la chiave. In ambienti ad alta sicurezza, dovresti confrontare l\u0026rsquo;impronta digitale sul Minion (salt-call key.finger) con quella sul Master (salt-key -F) prima di accettarla.\nPKI (Public Key Infrastructure): L\u0026rsquo;infrastruttura a chiave pubblica usata da Salt per gestire queste chiavi.\nCifratura (Encryption): Salt usa l\u0026rsquo;algoritmo AES-256 per il trasferimento effettivo dei dati, che è lo standard industriale per la protezione dei dati governativi e bancari.\n🛠️ Fare qualcosa di veramente utile Invece di eseguire comandi manuali, Salt è progettato per utilizzare i File di Stato (SLS). Questi descrivono l\u0026rsquo;aspetto che il sistema dovrebbe avere (configurazione dichiarativa).\nSulla tua macchina host (all\u0026rsquo;esterno del container), vai nella cartella ~/salt_lab/srv/salt e crea un file chiamato common_tools.sls:\ninstall_useful_packages: pkg.installed: - pkgs: - htop - ripgrep - fzf create_test_file: file.managed: - name: /etc/salt_was_here.txt - contents: | This minion is managed by SaltStack. Last updated: {{ salt[\u0026#39;system.get_system_date_time\u0026#39;]() }} - user: root - group: root - mode: \u0026#39;0644\u0026#39; Ora indica al Master di applicare quella configurazione al Minion.\npodman exec -it salt-master salt \u0026#39;salt-minion\u0026#39; state.apply common_tools (questa operazione potrebbe richiedere del tempo poiché comporta l\u0026rsquo;installazione dei pacchetti)\nUna volta ottenuto l\u0026rsquo;esito positivo con Succeeded: 2, puoi controllare il file all\u0026rsquo;interno del Minion per vedere il risultato del tuo lavoro:\npodman exec -it salt-minion cat /etc/salt_was_here.txt This minion is managed by SaltStack. Last updated: 2026-03-17 14:11:28 🤖 Mettere il laboratorio in autopilota Per fare in modo che il Minion rimanga sincronizzato automaticamente con i file del Master, utilizziamo una pianificazione Highstate. Invece di dover digitare state.apply, il Minion si collegherà (\u0026ldquo;check in\u0026rdquo;) ogni X minuti per verificare se la sua situazione reale corrisponde alle istruzioni del Master.\nCrea un nuovo file sul tuo host: ~/salt_lab/srv/salt/schedule.sls\n# Ensure the minion checks in every 5 minutes sync_with_master_periodically: schedule.present: - function: state.highstate - minutes: 5 Salt ha bisogno di un file top.sls per sapere che a ciascun Minion devono essere sempre applicati i propri stati. Crea ~/salt_lab/srv/salt/top.sls:\nbase: \u0026#39;*\u0026#39;: - common_tools - schedule Esegui questo comando una volta per indicare al Minion di avviare il proprio timer interno:\npodman exec -it salt-master salt \u0026#39;*\u0026#39; state.apply Ora, se aggiungi un nuovo pacchetto a common_tools.sls sul tuo host, non dovrai fare nulla. Entro 5 minuti, il Minion noterà la differenza e installerà automaticamente il pacchetto.\nIn un contesto GitOps strutturato, i file di stato saranno versionati in un repository, da cui il master potrà scaricarli e applicarli ai minion.\n🚀 La vita è troppo breve per comandi lunghi Digitare ogni volta podman exec -it salt-master ... può diventare noioso. Per far sì che il tuo laboratorio sembri un\u0026rsquo;installazione nativa, puoi aggiungere questi alias al tuo ~/.bashrc o ~/.zshrc:\n$ alias salt=\u0026#34;podman exec -it salt-master salt\u0026#34; $ alias salt-key=\u0026#34;podman exec -it salt-master salt-key\u0026#34; $ alias salt-run=\u0026#34;podman exec -it salt-master salt-run\u0026#34; $ alias salt-logs=\u0026#34;podman logs -f salt-master\u0026#34; $ alias minion-logs=\u0026#34;podman logs -f salt-minion\u0026#34; $ alias help-me-obi-wan=\u0026#34;salt \u0026#39;*\u0026#39; test.ping\u0026#34; Una volta configurati, potrai semplicemente eseguire salt-key -L or salt '*' test.ping direttamente dal terminale del tuo host.\nDopo aver configurato gli alias, puoi anche eseguire:\n$ salt \u0026#39;*\u0026#39; sys.doc Questo mostrerà una documentazione enorme e ricercabile di ogni singolo comando che Salt è in grado di eseguire. È come avere l\u0026rsquo;intero manuale integrato direttamente nel terminale.\n🎬 Questo è tutto Configurare un laboratorio SaltStack non deve necessariamente significare compromettere la sicurezza del sistema host o lottare con complesse reti di macchine virtuali. Sfruttando openSUSE Tumbleweed, Podman Rootless e i Quadlet, abbiamo creato un ambiente che è sicuro, dichiarativo e automatizzato.\nChe tu sia uno sviluppatore che desidera testare le modifiche alla configurazione localmente o un amministratore di sistema che si prepara per l\u0026rsquo;esame SaltStack Certified Engineer, questo approccio basato sui container fornisce un ambiente di prova rapido, usa-e-getta e di livello professionale.\nLa parte di \u0026ldquo;collegamento\u0026rdquo; è ormai conclusa. La tua rete è attiva, le tue chiavi sono state accettate e i tuoi minion sono pronti. L\u0026rsquo;unica domanda rimasta è: cosa automatizzerai la prossima volta?\nSfida Bonus: Prova ad aggiungere un secondo container minion alla tua rete saltnet. Riesci a usare i Grains per assicurarti che Apache venga installato solo sul primo minion e Nginx solo sul secondo?\n","permalink":"https://ilmanzo.github.io/it/post/rootless_podman_as_salt_lab_environment/","summary":"\u003ch2 id=\"-salt-senza-sudo\"\u003e🧂 Salt senza sudo\u003c/h2\u003e\n\u003cp\u003eIn continuità con \u003ca href=\"https://ilmanzo.github.io/it/post/podman_quadlets_tutorial/\"\u003eil post precedente\u003c/a\u003e, oggi metteremo all\u0026rsquo;opera i nostri container gestiti da systemd e li utilizzeremo per alcune attività utili.\u003c/p\u003e\n\u003cp\u003eL\u0026rsquo;idea è quella di configurare un ambiente per imparare come funziona il configuration management \u003ca href=\"https://saltproject.io/\"\u003eSalt\u003c/a\u003e e sperimentare/smanettare con esso, senza nemmeno aver bisogno dei permessi di root o di \u003ccode\u003esudo\u003c/code\u003e. Dopotutto, nel mondo delle infrastrutture, \u003cstrong\u003eil Salt deve scorrere\u003c/strong\u003e!\u003c/p\u003e\n\u003cp\u003eConfigureremo quindi due container, uno come \u003cem\u003esalt master\u003c/em\u003e e l\u0026rsquo;altro come \u003cem\u003esalt \u0026ldquo;minion\u0026rdquo;\u003c/em\u003e (che rappresenta la macchina che verrà configurata tramite salt).\u003c/p\u003e","title":"Podman rootless come ambiente di laboratorio per Salt"},{"content":"Intro Ieri, 21 febbraio, ho avuto il piacere di partecipare allo Zig Day Milano 2026, un fantastico evento dedicato al linguaggio di programmazione Zig. È stata una giornata intensa ricca di apprendimento, programmazione e incontri con persone fantastiche nella splendida cornice di Seregno (Milano).\n⚡ Cos\u0026rsquo;è Zig? Per chi ancora non lo conoscesse, Zig è un linguaggio di programmazione general-purpose e una toolchain per sviluppare software robusto, ottimizzato e riutilizzabile. Viene spesso visto come un moderno successore del C, ma offre molto di più.\nAlcuni dei suoi principali vantaggi che spiccano particolarmente sono:\nNessun flusso di controllo nascosto: se sembra una chiamata di funzione, allora si tratta di una chiamata di funzione. Nessuna sorpresa legata all\u0026rsquo;overload degli operatori. Nessuna allocazione di memoria nascosta: hai il pieno controllo della memoria. Se una funzione ha bisogno di allocare memoria, di solito accetta un allocatore come parametro. Comptime: una potente funzionalità che consente di eseguire codice Zig durante la compilazione. Sostituisce la necessità di preprocessori o macro e permette la programmazione generica in modo estremamente leggibile. Interoperabilità con il C: è possibile includere direttamente file header C e fare il link con librerie C senza alcuno sforzo. Cross-compilazione: il sistema di build zig build rende la compilazione incrociata per diverse architetture incredibilmente semplice. 🥐 Mattina: il Crash Course La giornata è iniziata nel migliore dei modi: con un\u0026rsquo;ottima colazione! Caffè e brioche sono stati il carburante ideale per dare il via alle attività.\nLa sessione mattutina è stata guidata da Loris Cro, VP of Community presso la Zig Software Foundation. Ci ha offerto un \u0026ldquo;crash course\u0026rdquo; sul linguaggio, approfondendo la filosofia alla base di Zig, la sintassi fondamentale e alcune delle caratteristiche uniche che lo contraddistinguono. È stato fantastico ascoltare le considerazioni di chi è coinvolto in prima persona nello sviluppo del linguaggio.\n🍝 Pranzo e Networking Dopo aver assimilato tutte queste informazioni, abbiamo fatto una pausa per il pranzo. È stata l\u0026rsquo;occasione perfetta per chiacchierare con gli altri partecipanti, discutere di quanto appreso e gustare un\u0026rsquo;ottima pizza.\n💻 Pomeriggio: Happy Hacking Il pomeriggio è stato dedicato all\u0026rsquo;hacking libero. Ci siamo divisi in gruppi o abbiamo lavorato individualmente su progetti open source.\nHo deciso di mettermi alla prova con ziglings, un progetto che insegna la sintassi e i concetti di Zig attraverso una serie di programmi volutamente errati da correggere. È un ottimo modo per imparare facendo, e lo consiglio vivamente a chiunque stia iniziando con Zig.\nÈ stato stimolante vedere tutti concentrati, pronti ad aiutarsi a vicenda e impegnati a creare progetti interessanti.\n🍕 Sera: Condivisione e Cena Al tramonto ci siamo riuniti per condividere i progressi compiuti. È stata una sessione di \u0026ldquo;show and tell\u0026rdquo; in cui i partecipanti hanno presentato i propri lavori pomeridiani. Dai semplici esercizi fino a strumenti più complessi, è stato davvero impressionante vedere cosa si può realizzare in poche ore.\nAbbiamo concluso l\u0026rsquo;evento con una cena, continuando le nostre conversazioni e festeggiando una giornata decisamente produttiva.\nConclusione Lo Zig Day Milano è stato un successo strepitoso. Se avete l\u0026rsquo;opportunità di partecipare a un evento Zig vicino a voi, non lasciatevela sfuggire! Si tratta di una community accogliente con uno stack tecnologico davvero entusiasmante.\nBuona programmazione! 🦎\n","permalink":"https://ilmanzo.github.io/it/post/zig_day_2026/","summary":"\u003ch1 id=\"intro\"\u003eIntro\u003c/h1\u003e\n\u003cp\u003eIeri, 21 febbraio, ho avuto il piacere di partecipare allo \u003ca href=\"https://zig.day/europe/milano/\"\u003e\u003cstrong\u003eZig Day Milano 2026\u003c/strong\u003e\u003c/a\u003e, un fantastico evento dedicato al linguaggio di programmazione Zig. È stata una giornata intensa ricca di apprendimento, programmazione e incontri con persone fantastiche nella splendida cornice di Seregno (Milano).\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"banner\" loading=\"lazy\" src=\"/img/zigday2026/zigdaybanner.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"-cosè-zig\"\u003e⚡ Cos\u0026rsquo;è Zig?\u003c/h2\u003e\n\u003cp\u003ePer chi ancora non lo conoscesse, \u003ca href=\"https://ziglang.org/\"\u003eZig\u003c/a\u003e è un linguaggio di programmazione general-purpose e una toolchain per sviluppare software robusto, ottimizzato e riutilizzabile. Viene spesso visto come un moderno successore del C, ma offre molto di più.\u003c/p\u003e","title":"Resoconto dello Zig Day 2026"},{"content":"🦎 Ciao geeko! Se hai eseguito container sulla tua macchina Leap o Tumbleweed, probabilmente avrai iniziato con i comandi podman run. Forse poi sei passato ai file Docker Compose per gestire gli stack. Si tratta di ottimi strumenti, ma hanno un limite: non si integrano nativamente con systemd, il sistema di init del tuo sistema operativo.\nQuando il tuo server si riavvia, i tuoi container ripartono automaticamente? Se un container va in crash, si riavvia? Come fai a visualizzare i suoi log insieme al journal di sistema?\nOggi esploreremo i Quadlet di Podman.\nI Quadlet rappresentano il modo moderno e \u0026ldquo;nativo\u0026rdquo; di eseguire i container Podman come veri e propri servizi systemd. Se ami openSUSE per la sua stabilità e la sua solida progettazione, adorerai i Quadlet. Trasformano le definizioni dei container in servizi di sistema estremamente robusti, senza richiedere la scrittura manuale di complessi file unit systemd da zero.\nPrepariamo la tua macchina openSUSE per utilizzare i Quadlet!\n👤 Come Root o come Utente? Nel mondo openSUSE attribuiamo grande valore alla sicurezza. Podman si distingue per il supporto ai container \u0026ldquo;rootless\u0026rdquo;, che consentono di eseguire container con il proprio utente standard, senza bisogno di usare sudo.\nAnche se è possibile eseguire i Quadlet come root (a livello di sistema), oggi ci concentreremo sui Quadlet rootless (utente). È una scelta più sicura, più semplice da gestire e non richiede privilegi elevati.\n🛠️ I prerequisiti su openSUSE Per prima cosa, assicurati che il tuo sistema sia aggiornato e che Podman sia installato.\nApri il terminale ed esegui:\n# For Leap or Tumbleweed sudo zypper refresh sudo zypper update sudo zypper install podman 🎬 Configurazione dell\u0026rsquo;ambiente Systemd deve sapere dove cercare questi file Quadlet. Per un utente rootless, esiste una directory specifica all\u0026rsquo;interno della cartella home. Di solito non esiste per impostazione predefinita, quindi creiamola. È qui che oggi avverrà tutta la magia.\nMYDIR=~/.config/containers/systemd/ mkdir -p $MYDIR \u0026amp;\u0026amp; cd $MYDIR Cos\u0026rsquo;è esattamente un Quadlet? Un Quadlet è semplicemente un file di testo simile a un file INI. Al suo interno descrivi ciò di cui hai bisogno (immagine del container, porte, volumi) e systemd utilizzerà un generatore per convertire quel file in una vera e propria unità di servizio dietro le quinte.\nLa tipologia più comune di file Quadlet ha estensione .container.\n👋 Esempio 1: il server web \u0026ldquo;Hello World\u0026rdquo; (Caddy) Iniziamo con qualcosa di semplice. Vogliamo eseguire il server web Caddy. Desideriamo che si avvii automaticamente al boot, si riavvii in caso di crash e rimanga in ascolto sulla porta 8080.\nCrea un nuovo file all\u0026rsquo;interno di ~/.config/containers/systemd/ chiamato myserver.container.\nPuoi usare nano, [neo]vim, micro o il tuo editor di testo grafico preferito.\nFile: ~/.config/containers/systemd/myserver.container\n[Unit] Description=My Caddy Web Server # Wait until networking is up before starting After=network-online.target [Container] # The image to use (always good practice to specify the registry) Image=docker.io/library/caddy:latest # Map host port 8080 to container port 80 PublishPort=8080:80 # Mount a volume for persistent data. # The \u0026#39;:Z\u0026#39; is important for SELinux on openSUSE! Volume=caddy-data:/data:Z [Service] # If it crashes, restart it Restart=always # Give it time to pull heavy images on slower connections TimeoutStartSec=600 [Install] # This makes sure it starts when your user session starts (or boot via linger) WantedBy=default.target Guarda com\u0026rsquo;è leggibile! È decisamente più pulito rispetto a un enorme comando podman run tenuto insieme da barre rovesciate (\\).\n✨ La fase di attivazione \u0026ldquo;magica\u0026rdquo; Se esegui podman ps in questo momento, non vedrai nulla in esecuzione. Hai creato le definizioni, ma systemd non sa ancora che esistono.\nDobbiamo indicare a systemd di scansionare la nostra directory di configurazione e generare le effettive unità di servizio. Poiché operiamo in modalità rootless, utilizziamo il flag --user.\nEsegui questo comando nel tuo terminale:\nsystemctl --user daemon-reload Controlla i log del journal; se non compaiono messaggi di errore, significa che ha funzionato:\nSystemd ha preso il tuo file myserver.container e ha generato silenziosamente un servizio chiamato myserver.service.\nAvvia il server web:\nsystemctl --user start myserver.service (questa operazione richiederà un po\u0026rsquo; di tempo al primo avvio, poiché podman deve scaricare l\u0026rsquo;immagine del container)\nControlla lo stato del servizio:\nsystemctl --user status myserver.service Dovresti vedere la scritta verde che riporta \u0026ldquo;active (running)\u0026rdquo;.\nMettilo alla prova! Apri Firefox e visita http://localhost:8080. Dovresti visualizzare la pagina iniziale predefinita di Caddy.\n$ curl -I http://localhost:8080 HTTP/1.1 200 OK Accept-Ranges: bytes Content-Length: 18753 Content-Type: text/html; charset=utf-8 Etag: \u0026#34;dfzwznr2vfggegx\u0026#34; Last-Modified: Wed, 28 Jan 2026 03:49:55 GMT Server: Caddy Vary: Accept-Encoding Date: Tue, 03 Feb 2026 10:00:49 GMT 🗄️ Esempio 2: il database (MariaDB con Secret) Le applicazioni reali solitamente hanno bisogno di un database, e non si dovrebbero mai inserire le password direttamente nel file di configurazione principale.\nConfiguriamo MariaDB, uno dei preferiti nell\u0026rsquo;ecosistema openSUSE, e passiamo le credenziali in modo sicuro utilizzando un file di ambiente separato.\n1. Creare il file secret Crea un file chiamato mariadb.env nella stessa directory.\nFile: ~/.config/containers/systemd/mariadb.env\nMYSQL_ROOT_PASSWORD=SuperSecretOpenSUSEPassword! MYSQL_DATABASE=myappdb MYSQL_USER=appuser MYSQL_PASSWORD=apppassword Suggerimento per la sicurezza: su un sistema reale, esegui chmod 600 mariadb.env in modo che solo il tuo utente possa leggere questo file!\n2. Creare il file del Container Ora crea il file Quadlet che fa riferimento a quelle credenziali:\nFile: ~/.config/containers/systemd/mydb.container\n[Unit] Description=MariaDB Database Service After=network-online.target [Container] Image=docker.io/library/mariadb:11.8 ContainerName=production-db # Tell Podman where to find the environment variables EnvironmentFile=%h/.config/containers/systemd/mariadb.env # Persistent storage for the database files Volume=mysql-data:/var/lib/mysql:Z # We usually don\u0026#39;t publish DB ports to the outside world, # but this is just a test example. DO NOT DO THIS IN PRODUCTION! PublishPort=127.0.0.1:3306:3306 [Service] Restart=on-failure TimeoutStartSec=600 [Install] WantedBy=default.target Nota: hai notato %h nel percorso del file? Si tratta di un identificatore di systemd che viene sostituito automaticamente con il percorso della tua directory home (ad esempio, /home/geeko).\n🤹 Gestire i tuoi nuovi servizi Ora puoi gestire questi container esattamente come fai per tutti gli altri servizi di sistema su openSUSE (come Apache, SSH o firewalld) utilizzando il comando systemctl.\nAvvia il database:\nsystemctl --user daemon-reload \u0026amp;\u0026amp; systemctl --user start mydb.service Visualizzare i log (alla maniera di systemd):\njournalctl --user -u mydb.service Non avrai più bisogno di usare podman logs. Sfrutta la potenza del journal! Per seguire i log in tempo reale:\njournalctl --user -f -u mydb.service Verifichiamo se funziona:\n$ zypper in mariadb-client $ mariadb -h localhost -u appuser --protocol=TCP --skip-ssl -p Enter password: Welcome to the MariaDB monitor. Commands end with ; or \\g. Your MariaDB connection id is 6 Server version: 10.11.15-MariaDB-ubu2204 mariadb.org binary distribution Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others. Type \u0026#39;help;\u0026#39; or \u0026#39;\\h\u0026#39; for help. Type \u0026#39;\\c\u0026#39; to clear the current input statement. MariaDB [(none)]\u0026gt; use myappdb ; show tables; Database changed Empty set (0.000 sec) Per un vero database pronto per la produzione avresti ovviamente bisogno di alcune ottimizzazioni extra (definire i permessi corretti per i volumi di dati, abilitare SSL, configurare la replica o i backup, ecc.), ma il concetto è chiaro \u0026#x1f604;\n🥾 Il tocco finale: avvio al boot Al momento, se riavvii la tua macchina openSUSE, questi container non si avvieranno finché non effettuerai l\u0026rsquo;accesso tramite interfaccia grafica o SSH.\nPer far sì che i container rootless si avviino istantaneamente all\u0026rsquo;accensione del server (ancora prima che tu effettui il login), devi abilitare il \u0026ldquo;lingering\u0026rdquo; per il tuo account utente.\nPer impostazione predefinita, le istanze utente di systemd vengono avviate al momento del login e arrestate al logout. Abilitare il lingering indica a systemd di avviare il gestore dei servizi utente al boot e mantenerlo in esecuzione anche quando non sei connesso. Questo è fondamentale per i server, poiché garantisce che i tuoi container vengano avviati subito all\u0026rsquo;accensione della macchina, senza attendere l\u0026rsquo;apertura di una sessione utente.\nEsegui questo comando una volta:\n# Replace \u0026#39;geeko\u0026#39; with your actual username sudo loginctl enable-linger geeko Ora abilita i tuoi servizi in modo che si avviino automaticamente:\nsystemctl --user enable myserver.service systemctl --user enable mydb.service Suggerimento: a scopo di debug, puoi sempre ispezionare e leggere i file unit di systemd generati guardando in /var/run/user/$UID/systemd/generator:\n$ ls -l /var/run/user/1000/systemd/generator drwxr-xr-x. 2 andrea andrea 80 Feb 3 11:50 default.target.wants -rw-r--r--. 1 andrea andrea 1335 Feb 3 11:50 mydb.service -rw-r--r--. 1 andrea andrea 1320 Feb 3 11:50 myserver.service Questo è tutto per ora! Ora hai un ambiente di container flessibile, profondamente integrato nel tuo sistema openSUSE. In un prossimo post esploreremo funzionalità più avanzate, come la rete interna (comunicazione container-to-container), i limiti sulle risorse, gli health check, la gestione dei secret e altro ancora. Buon divertimento con Podman!\n","permalink":"https://ilmanzo.github.io/it/post/podman_quadlets_tutorial/","summary":"\u003ch2 id=\"-ciao-geeko\"\u003e🦎 Ciao geeko!\u003c/h2\u003e\n\u003cp\u003eSe hai eseguito container sulla tua macchina \u003ca href=\"https://get.opensuse.org/leap\"\u003eLeap\u003c/a\u003e o \u003ca href=\"https://get.opensuse.org/tumbleweed/\"\u003eTumbleweed\u003c/a\u003e, probabilmente avrai iniziato con i comandi \u003ccode\u003epodman run\u003c/code\u003e. Forse poi sei passato ai file \u003ccode\u003eDocker Compose\u003c/code\u003e per gestire gli stack. Si tratta di ottimi strumenti, ma hanno un limite: non si integrano nativamente con \u003ccode\u003esystemd\u003c/code\u003e, il sistema di init del tuo sistema operativo.\u003c/p\u003e\n\u003cp\u003eQuando il tuo server si riavvia, i tuoi container ripartono automaticamente? Se un container va in crash, si riavvia? Come fai a visualizzare i suoi log insieme al journal di sistema?\u003c/p\u003e","title":"Una guida pratica a Podman Quadlet"},{"content":"👻 Void in taverna Ho una taverna e ho un problema: in questa taverna non arriva il segnale Wi-Fi. Ho anche un pezzo di spazzatura elettronica che si rifiuta di morire: un netbook Samsung N130 del 2009. Ha un processore Atom single-core e 1 GB di RAM. Per gli standard moderni, riesce a malapena ad aprire un browser web. Ma per un terminale Linux, è un supercomputer. Invece di acquistare un generico ripetitore Wi-Fi da una decina di euro, ho deciso di trasformare questo piccolo guerriero in un router Wi-Fi completamente programmabile, sicuro e trasparente utilizzando Void Linux. Ecco esattamente come ho fatto.\n💻 La configurazione hardware Samsung N130 (scheda Wi-Fi interna: Atheros/Realtek a seconda del modello). Una chiavetta Wi-Fi USB economica (Realtek RTL8188EUS) che accumulava polvere in un cassetto. Void Linux (versione base, glibc). Il piano è semplice:\nLa chiavetta USB si connette alla rete Wi-Fi principale di casa al piano superiore. La scheda interna trasmette una nuova rete al piano inferiore. Il netbook instrada il traffico tra le due reti. 🐧 0: Installazione di Void Linux Nulla di troppo complicato: basta creare una chiavetta USB dall\u0026rsquo;immagine ISO, avviarla e seguire l\u0026rsquo;eccellente documentazione. Preferisco installare anche alcuni strumenti standard di uso quotidiano come zsh, fzf, ripgrep, starship, zoxide, fdfind e alcuni pacchetti extra di cui avremo bisogno in seguito: wpa_supplicant, dnsmasq, hostapd, cronie, nftables, ttyqr.\nPerché proprio Void Linux? Beh, è disponibile per architetture a 32 bit, è costantemente aggiornata ed è estremamente parca nei consumi di risorse. Offre un\u0026rsquo;esperienza d\u0026rsquo;uso Linux vecchio stile, lineare e semplice.\n🛜 1: Scelta della scheda (Modalità AP) Non tutte le schede Wi-Fi sono uguali. Per funzionare come hotspot, una scheda deve supportare la modalità AP (Access Point).\nHo installato iw e controllato entrambe le schede:\niw list Ho cercato l\u0026rsquo;elenco Supported interface modes nell\u0026rsquo;output.\nSe contiene la voce AP, siamo a posto.\nSe riporta solo managed, quella scheda può funzionare solo come client (da usare per il Gateway).\nNel mio caso, la scheda interna supportava perfettamente la modalità AP, quindi è diventata l\u0026rsquo;Access Point, mentre la chiavetta USB è diventata il Gateway.\n😵 2: Rinominare le interfacce (Udev Rules) I nomi delle interfacce Linux come wlp2s0 o wlp0s29f7u1\u0026hellip; sono impossibili da ricordare. Rinominiamoli in wlan_ap (interna) e wlan_gw (esterna/USB) così da non fare mai confusione. Ho creato il file /etc/udev/rules.d/10-network.rules:\n# Internal Card -\u0026gt; wlan_ap SUBSYSTEM==\u0026#34;net\u0026#34;, ACTION==\u0026#34;add\u0026#34;, ATTR{address}==\u0026#34;00:11:22:33:44:55\u0026#34;, NAME=\u0026#34;wlan_ap\u0026#34; # USB Dongle -\u0026gt; wlan_gw SUBSYSTEM==\u0026#34;net\u0026#34;, ACTION==\u0026#34;add\u0026#34;, ATTR{address}==\u0026#34;aa:bb:cc:dd:ee:ff\u0026#34;, NAME=\u0026#34;wlan_gw\u0026#34; (Suggerimento: ottieni i tuoi indirizzi MAC usando il comando ip link).\nDopo il riavvio, il comando ip link ha mostrato i miei nuovi e ordinati nomi.\nSe preferisci evitare il riavvio, puoi attivare le regole udev con:\nudevadm trigger --verbose --subsystem-match=net --action=add 🔌 3: Connessione a monte (Il Client) Ho usato wpa_supplicant per connettere la chiavetta USB alla rete Wi-Fi principale di casa.\nFile: /etc/wpa_supplicant/wpa_supplicant.conf\nctrl_interface=/run/wpa_supplicant update_config=1 network={ ssid=\u0026#34;MyUpstairsWiFi\u0026#34; psk=\u0026#34;SuperSecretHomePassword\u0026#34; } Il parametro psk è una lunga stringa esadecimale, che puoi ottenere con: wpa_passphrase \u0026quot;IL_TUO_SSID\u0026quot; \u0026quot;LA_TUA_PASSWORD\u0026quot; | sudo tee /etc/wpa_supplicant/wpa_supplicant.conf\nSuccessivamente, ho configurato il servizio. Poiché su Void utilizzo runit, ho configurato il file di esecuzione del servizio per forzarlo a utilizzare wlan_gw:\ncat /var/service/wpa_supplicant/conf WPA_INTERFACE=wlan_gw Su Void, per abilitare un servizio e farlo eseguire all\u0026rsquo;avvio da runit, è sufficiente creare un collegamento simbolico (symlink):\nln -s /etc/sv/wpa_supplicant /var/service/ 🔥 4: L\u0026rsquo;Hotspot (hostapd) Ora passiamo alla trasmissione della rete per gli ospiti. Ho installato hostapd e l\u0026rsquo;ho configurato per trasformare la scheda interna in un hotspot.\nFile: /etc/hostapd/hostapd.conf\ninterface=wlan_ap driver=nl80211 ssid=Basement_Bunker hw_mode=g channel=6 wmm_enabled=0 macaddr_acl=0 auth_algs=1 ignore_broadcast_ssid=0 wpa=3 wpa_passphrase=BasementPassword123 wpa_key_mgmt=WPA-PSK rsn_pairwise=CCMP ieee80211n=1 Ho abilitato anche questo servizio (ln -s /etc/sv/hostapd /var/service/). E subito la rete è apparsa sul mio telefono! Ma connettersi ad essa non portava ancora a nulla.\n📟 5: Il cervello (IP e DHCP) Dobbiamo assegnare un IP statico sia all\u0026rsquo;Access Point che al Gateway.\nFile: /etc/dhcpcd.conf\n# Gateway act as a plain wifi client device interface wlan_gw static ip_address=192.168.1.99/24 static routers=192.168.1.1 # main home router with internet connection # Access Point: Static IP (I am the Captain now) interface wlan_ap static ip_address=192.168.50.1/24 In seguito, dnsmasq gestisce sia l\u0026rsquo;assegnazione degli IP ai dispositivi che si connettono alla rete in taverna, sia la risoluzione dei nomi con relativa cache.\nFile: /etc/dnsmasq.conf\n# Listen only on the local interface (AP) interface=wlan_ap interface=lo bind-interfaces cache-size=1000 domain-needed bogus-priv dhcp-range=192.168.50.100,192.168.50.200,255.255.255.0,12h server=192.168.1.1 server=8.8.8.8 # Set the default gateway and DNS for clients dhcp-option=3,192.168.50.1 dhcp-option=6,192.168.50.1 🪠 6: I collegamenti (Routing e NAT) Il kernel deve essere autorizzato a far transitare i pacchetti da un\u0026rsquo;interfaccia all\u0026rsquo;altra.\nFile: /etc/sysctl.d/99-forwarding.conf\nnet.ipv4.ip_forward=1 Infine, nftables si occupa del lavoro più pesante: NAT (masquerading), regole di firewall e l\u0026rsquo;importante correzione MSS Clamping (senza la quale i telefoni Android si connettono ma non riescono a caricare i siti web). Ho preferito questo strumento rispetto al classico iptables per mettermi alla prova con qualcosa di più moderno.\nFile: /etc/nftables.conf\n#!/usr/sbin/nft -f flush ruleset table ip nat { chain postrouting { type nat hook postrouting priority 100; policy accept; # Masquerade traffic leaving the USB dongle oifname \u0026#34;wlan_gw\u0026#34; masquerade } } table inet filter { chain input { type filter hook input priority 0; policy drop; iifname \u0026#34;lo\u0026#34; accept ct state established,related accept ip protocol icmp accept # Allow DHCP \u0026amp; DNS from the basement iifname \u0026#34;wlan_ap\u0026#34; udp dport { 67, 53 } accept iifname \u0026#34;wlan_ap\u0026#34; tcp dport 53 accept # SECURITY: Block SSH from the basement! # Only allow SSH from the main house network (Gateway) # Assuming main house is 192.168.1.x iifname \u0026#34;wlan_gw\u0026#34; ip saddr 192.168.1.0/24 tcp dport 22 accept } chain forward { type filter hook forward priority 0; policy drop; # TCP MSS Clamping: The magic fix for WiFi-to-WiFi bridging tcp flags syn tcp option maxseg size set rt mtu # Allow traffic flow iifname \u0026#34;wlan_ap\u0026#34; oifname \u0026#34;wlan_gw\u0026#34; accept iifname \u0026#34;wlan_gw\u0026#34; oifname \u0026#34;wlan_ap\u0026#34; ct state established,related accept } chain output { type filter hook output priority 0; policy accept; } } 🤳 7: Un tocco \u0026ldquo;User Friendly\u0026rdquo; (Accesso tramite QR Code) Dato che questo netbook sta appoggiato su uno scaffale, volevo che lo schermo mostrasse qualcosa di utile. Al posto del solito e noioso prompt di login, gli faccio visualizzare un QR code in modo che gli ospiti possano scansionarlo per connettersi (scan-to-connect).\nHo installato ttyqr e ho aggiunto questo frammento al file /etc/rc.local:\n# Clear screen echo -e \u0026#34;\\033c\u0026#34; \u0026gt; /etc/issue # Generate QR Code for WiFi # Format: WIFI:T:WPA;S:MySSID;P:MyPassword;; ttyqr -t ANSIUTF8 \u0026#34;WIFI:T:WPA;S:Basement_Bunker;P:BasementPassword123;;\u0026#34; \u0026gt;\u0026gt; /etc/issue # Add text echo -e \u0026#34;\\nScan to join the Bunker!\u0026#34; \u0026gt;\u0026gt; /etc/issue echo -e \u0026#34;IP: 192.168.50.1\u0026#34; \u0026gt;\u0026gt; /etc/issue Ora la schermata di login TTY ha l\u0026rsquo;aspetto di un chiosco interattivo!\n(non scansionare questo: la rete e la password mostrate qui non sono reali)\n👾 Cos\u0026rsquo;altro si può fare con 1 GB di RAM? 🔊 Poiché la parte router consuma pochissime risorse (meno di 100 MB di RAM), ho deciso di affidare all\u0026rsquo;N130 qualche altro compito.\nJukebox (mpd + ncmpcpp) Ho installato mpd (Music Player Daemon) e ho collegato il netbook a un vecchio altoparlante tramite il jack delle cuffie. In questo modo può riprodurre file musicali locali (disponendo di oltre 100 GB di spazio di archiviazione) e flussi radio internet senza problemi.\nPosso controllare il volume e le stazioni radio via SSH o tramite un\u0026rsquo;applicazione sul telefono, oppure usare l\u0026rsquo;analizzatore di spettro in ncmpcpp direttamente sullo schermo del netbook.\nStazione per Retro Gaming Quando internet va via, posso comunque passare il tempo. Solo qualche esempio:\nBastet: un clone spietato di Tetris.\nNinvaders: Space Invaders nel terminale.\nMoon-buggy: un gioco di guida a scorrimento orizzontale.\nPacman4Console: Waka Waka\nTutti questi giochi girano alla perfezione in modalità testo (TTY), richiedendo zero interfaccia grafica (niente Wayland né X11), risparmiando così preziosa memoria RAM per il routing.\n📜 Verdetto finale Il mio vecchio N130 è tornato in vita. Si avvia in pochi secondi, gestisce il traffico per l\u0026rsquo;intera taverna, blocca i tentativi SSH non autorizzati, si aggiorna automaticamente e aiuta persino gli ospiti a connettersi con un QR code. Non male per un portatile che già 10 anni fa era considerato \u0026ldquo;too slow\u0026rdquo;.\nIn conclusione\u0026hellip; Sì, avrei potuto semplicemente acquistare un ripetitore Wi-Fi invece di passare due ore a configurare un vecchio PC, ma nel frattempo ho imparato molto e salvato dell\u0026rsquo;hardware dalla discarica. Buon hacking!\n","permalink":"https://ilmanzo.github.io/it/post/repurpose_old_netbook_as_wifi_repeater/","summary":"\u003ch2 id=\"-void-in-taverna\"\u003e👻 Void in taverna\u003c/h2\u003e\n\u003cp\u003eHo una taverna e ho un problema: in questa taverna non arriva il segnale Wi-Fi. Ho anche un pezzo di spazzatura elettronica che si rifiuta di morire: un netbook \u003ca href=\"https://en.wikipedia.org/wiki/Samsung_N130\"\u003e\u003cstrong\u003eSamsung N130 del 2009\u003c/strong\u003e\u003c/a\u003e.\nHa un processore Atom single-core e 1 GB di RAM. Per gli standard moderni, riesce a malapena ad aprire un browser web. Ma per un terminale Linux, è un \u003cem\u003esupercomputer\u003c/em\u003e.\nInvece di acquistare un generico ripetitore Wi-Fi da una decina di euro, ho deciso di trasformare questo piccolo guerriero in un router Wi-Fi completamente programmabile, sicuro e trasparente utilizzando \u003ca href=\"https://voidlinux.org/\"\u003e\u003cstrong\u003eVoid Linux\u003c/strong\u003e\u003c/a\u003e. Ecco esattamente come ho fatto.\u003c/p\u003e","title":"Creazione di un router Linux headless"},{"content":"🎉 Buon anno nuovo! Come sa chiunque lavori con openQA, si tratta di uno strumento estremamente potente per i test automatizzati. Tuttavia, a volte il flusso di lavoro necessario per riattivare i test a scopo di analisi può sembrare un po\u0026rsquo;\u0026hellip; manuale.\nDi recente mi sono ritrovato in un ciclo ripetitivo durante il debug di scenari di test complessi. Il mio flusso di lavoro si presentava all\u0026rsquo;incirca così:\nIndividuare l\u0026rsquo;URL di un job noto come \u0026ldquo;corretto\u0026rdquo;. Aprire un editor di appunti veloce. Comporre un lungo comando openqa-clone-job con una marea di parametri di override specifici (BUILD=0, branch Git personalizzati, esclusione di moduli specifici, ecc.). Eseguire il comando nel terminale e attendere l\u0026rsquo;output. Scansionare visivamente con fatica l\u0026rsquo;output del terminale per individuare gli URL dei nuovi job creati, selezionandoli con il mouse per copiarli. Incollarli in openqa-mon o in un file di testo per monitorarne l\u0026rsquo;avanzamento. Fino a questo momento ho utilizzato uno script Bash che in un certo senso mi aiutava, ma modificare array giganteschi di argomenti per ogni diverso scenario di debug era noioso e incline all\u0026rsquo;errore.\nPoi ho capito che dovevo separare la configurazione (il cosa — quali job e quali parametri) dalla logica di esecuzione (il come — l\u0026rsquo;esecuzione del comando e l\u0026rsquo;estrazione dei risultati).\nEcco come sono passato da un fragile script Bash a un flusso di lavoro di automazione robusto basato su Python e YAML.\n🎯 \u0026ldquo;Infrastructure as Code\u0026rdquo; per i test ad-hoc Volevo un sistema in cui poter definire uno scenario di test all\u0026rsquo;interno di un file pulito e leggibile, eseguire un unico comando e avere immediatamente gli URL dei job risultanti pronti per essere monitorati.\nFase 1: Definire la configurazione (YAML) Invece di scrivere le variabili in modo rigido all\u0026rsquo;interno dello script, le ho spostate in un file YAML strutturato. Questo rende incredibilmente facile capire esattamente cosa debba fare una determinata esecuzione di test.\nEcco un esempio di file di configurazione, che chiameremo krb5_ssh_test.yaml:\n# krb5_ssh_test.yaml # The parent jobs to clone from jobs_to_clone: - https://openqa.opensuse.org/tests/123456 - https://openqa.opensuse.org/tests/789012 # Command line flags flags: - \u0026#34;--clone-children\u0026#34; - \u0026#34;--skip-deps\u0026#34; # Environment variables and parameters variables: _GROUP_ID: 38 BUILD: \u0026#34;my-custom-build\u0026#34; # Pointing to my custom test branch CASEDIR: \u0026#34;https://github.com/ilmanzo/os-autoinst-distri-opensuse.git#my_custom_branch\u0026#34; _SKIP_POST_FAIL_HOOKS: 1 QEMURAM: 2048 Questo formato è leggibile, può essere tracciato con sistemi di controllo versione ed è facile da copiare e modificare per scenari differenti.\nFase 2: La logica di automazione (Python) Se da un lato Bash è ottima per concatenare comandi, dall\u0026rsquo;altro Python eccelle nel parsing di dati strutturati (YAML) e nella manipolazione di testo (Regex).\nHo scritto uno script Python clone_runner.py che si occupa principalmente di tre cose:\nLeggere la configurazione YAML e costruire dinamicamente gli argomenti per il comando openqa-clone-job. Eseguire il comando in modo sicuro usando il modulo subprocess di Python. Effettuare il parsing dell\u0026rsquo;output con precisione. Questo è stato il miglioramento chiave: invece di dover analizzare manualmente il testo nel terminale, Python usa le espressioni regolari per individuare righe come -\u0026gt; https://... ed estrarre automaticamente i nuovi URL. Ecco la funzione regex cruciale che ha sostituito il mio copia-incolla manuale:\ndef extract_urls(output_text: str) -\u0026gt; List[str]: \u0026#34;\u0026#34;\u0026#34;Parses output looking for: \u0026#39;- jobname -\u0026gt; https://url...\u0026#39; \u0026#34;\u0026#34;\u0026#34; url_pattern = re.compile(r\u0026#34;-\u0026gt;\\s+(https?://\\S+)\u0026#34;) return url_pattern.findall(output_text) Lo script assegna anche automaticamente il nome al file di output in base al nome del file di configurazione. Se utilizzo i parametri di krb5_ssh_test.yaml, genererà krb5_ssh_test.urls.txt.\n😇 Il nuovo flusso di lavoro Ora il mio flusso di lavoro è snello e coerente.\nMi basta eseguire lo script indicando il file di configurazione:\n$ ./clone_runner.py -c krb5_ssh_test.yaml Output:\n- Starting clone process using config: krb5_ssh_test.yaml - Output will be saved to: krb5_ssh_test.urls.txt Processing: https://openqa.suse.de/tests/20438098 - Extracted 4 new job URLs. Processing: https://openqa.suse.de/tests/20394793 - Extracted 6 new job URLs. ======================================== Success! URLs saved to \u0026#39;krb5_ssh_test.urls.txt\u0026#39; You can now run: openqa-mon -i krb5_ssh_test.urls.txt ======================================== L\u0026rsquo;ultimo passaggio consiste nel passare i dati direttamente allo strumento di monitoraggio:\n$ openqa-mon -i krb5_ssh_test.urls.txt 🪚 Conclusione Dedicando un po\u0026rsquo; di tempo al passaggio da un imperativo script Bash a una configurazione dichiarativa in YAML gestita tramite Python, ho eliminato le parti più noiose e soggette a errori dell\u0026rsquo;avvio dei test ad-hoc su openQA. Si tratta di un piccolo miglioramento nell\u0026rsquo;automazione che ripaga ogni singolo giorno, consentendomi di concentrarmi sui risultati dei test anziché sugli argomenti della riga di comando. Ti invito a dare un\u0026rsquo;occhiata al progetto e a contribuire su https://github.com/ilmanzo/openqa-clone-runner\n","permalink":"https://ilmanzo.github.io/it/post/automating_job_cloning/","summary":"\u003ch2 id=\"-buon-anno-nuovo\"\u003e🎉 Buon anno nuovo!\u003c/h2\u003e\n\u003cp\u003eCome sa chiunque lavori con \u003ca href=\"https://open.qa/\"\u003eopenQA\u003c/a\u003e, si tratta di uno strumento estremamente potente per i test automatizzati. Tuttavia, a volte il flusso di lavoro necessario per riattivare i test a scopo di analisi può sembrare un po\u0026rsquo;\u0026hellip; \u003cem\u003emanuale\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eDi recente mi sono ritrovato in un ciclo ripetitivo durante il debug di scenari di test complessi. Il mio flusso di lavoro si presentava all\u0026rsquo;incirca così:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eIndividuare l\u0026rsquo;URL di un job noto come \u0026ldquo;corretto\u0026rdquo;.\u003c/li\u003e\n\u003cli\u003eAprire un editor di appunti veloce.\u003c/li\u003e\n\u003cli\u003eComporre un lungo comando \u003ccode\u003eopenqa-clone-job\u003c/code\u003e con una marea di parametri di override specifici (\u003ccode\u003eBUILD=0\u003c/code\u003e, branch Git personalizzati, esclusione di moduli specifici, ecc.).\u003c/li\u003e\n\u003cli\u003eEseguire il comando nel terminale e attendere l\u0026rsquo;output.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eScansionare visivamente con fatica l\u0026rsquo;output del terminale\u003c/strong\u003e per individuare gli URL dei nuovi job creati, selezionandoli con il mouse per copiarli.\u003c/li\u003e\n\u003cli\u003eIncollarli in \u003ca href=\"https://github.com/os-autoinst/openqa-mon\"\u003e\u003ccode\u003eopenqa-mon\u003c/code\u003e\u003c/a\u003e o in un file di testo per monitorarne l\u0026rsquo;avanzamento.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eFino a questo momento ho utilizzato uno script \u003ccode\u003eBash\u003c/code\u003e che in un certo senso mi aiutava, ma modificare array giganteschi di argomenti per ogni diverso scenario di debug era noioso e incline all\u0026rsquo;errore.\u003c/p\u003e","title":"Automatizzare la clonazione dei job OpenQA con Python e YAML"},{"content":"🎄 Intro È dicembre, il periodo dell\u0026rsquo;anno più bello per i programmatori. Ma effettuando l\u0026rsquo;accesso all\u0026rsquo;Advent of Code (AoC) 2025, potresti notare che l\u0026rsquo;atmosfera è leggermente diversa. Abbiamo superato un decennio dello straordinario lavoro di Eric Wastl, e con questo traguardo arriva un importante cambiamento nella tradizione.\nPrima di tuffarmi nelle soluzioni, voglio prendermi un momento per riflettere sullo stato dell\u0026rsquo;AoC di quest\u0026rsquo;anno, sui cambiamenti che stiamo vedendo e sul perché — nonostante tutto — continuiamo a tornare davanti al terminale.\nI cambiamenti nel formato del 2025 Se stai cercando la classifica globale (Global Leaderboard) o ti stai preparando per una maratona di 25 giorni, avrai probabilmente notato due importanti novità:\nNessuna classifica globale: la frenesia competitiva è stata rimossa quest\u0026rsquo;anno. Un calendario di 12 giorni: invece dei soliti 25 giorni, l\u0026rsquo;evento dura 12 giorni. Sebbene questi cambiamenti possano sorprendere i veterani, portano con sé un messaggio di empatia. Mantenere un progetto di questa portata per dieci anni è un impegno colossale e massacrante. Eric ha costantemente lavorato a livelli incredibili per offrirci queste sfide così eleganti, divertenti e creative. Riconoscere il \u0026ldquo;costo umano\u0026rdquo; di questo evento significa supportare la necessità del creatore di tutelare il proprio tempo e la propria salute mentale.\nPerché continuiamo a risolvere i puzzle Nel 2025, in mezzo al gran parlare di intelligenza artificiale e assistenti di scrittura codice, sorge una domanda spontanea: \u0026ldquo;Perché scomodarsi a risolvere puzzle quando un\u0026rsquo;IA può farlo in pochi secondi?\u0026rdquo;\nLa risposta è semplice: le persone continuano ad andare ai musical e ai concerti dal vivo, anche se esiste Spotify.\nNon partecipiamo all\u0026rsquo;Advent of Code perché è la via più \u0026ldquo;efficiente\u0026rdquo; per ottenere una risposta. Lo facciamo perché vogliamo risolvere il puzzle. Lo facciamo per l\u0026rsquo;emozione, la frustrazione e l\u0026rsquo;apprendimento. L\u0026rsquo;AoC è un modo per riconnettersi con il puro amore per la programmazione. Si rivolge a qualsiasi livello di competenza, dal principiante alle prime armi fino allo sviluppatore esperto in cerca di uno stimolo.\nUna tradizione generazionale Che si tratti di 25 o di 12 giorni, l\u0026rsquo;Advent of Code è dventato per molti di noi una tradizione forte tanto quanto Star Wars. È qualcosa da tramandare; ci sono bambini che oggi indossano il pigiama a tema AoC, crescendo con questi enigmi come punto fermo delle festività natalizie.\nQuindi, grazie a Eric per gli ultimi 10 anni. Noi siamo qui per gli enigmi, la community e la tradizione — in qualsiasi formato tu preferisca.\nE ora, apriamo l\u0026rsquo;editor e risolviamo il Giorno 1. ATTENZIONE SPOILER!\n⏰ Giorno 1 : La cassaforte Oh no, a quanto pare gli Elfi hanno scoperto il Project Management! (Sospetto che questo sia un indizio sul numero ridotto di stelle di quest\u0026rsquo;anno, che Eric abbia cambiato ruolo?)\nAbbiamo una cassaforte con una manopola a 100 posizioni (da 0 a 99) e le istruzioni per ruotarla a sinistra (Left) e a destra (Right) per un certo numero di volte: L68 L30 R48 L5 R60 L55 L1 L99 R14 L82 e così via. La posizione iniziale della manopola è 50. Nella prima parte, devi calcolare quante volte la manopola si FERMA esattamente sul numero 0; nella seconda parte (svelata dopo la prima soluzione) devi calcolare quante volte è PASSATA per il numero 0.\n(animazione per gentile concessione di https://www.reddit.com/user/Disastrous-Funny-781/)\nEcco un\u0026rsquo;elegante soluzione in AWK:\nBEGIN { p = 50 } { c = substr($0, 2) p = (p + (substr($0, 1, 1) == \u0026#34;R\u0026#34; ? c : 100 - c)) % 100 n += !p } END { print n } Questa soluzione sfrutta l\u0026rsquo;aritmetica modulare: ruotare a sinistra (Left) di N è equivalente a ruotare a destra (Right) di 100-N.\n🎁 Giorno 2 : Il negozio di regali Alcuni elfi più giovani hanno giocato con il computer del negozio di regali, incasinando gli ID dei prodotti!\nTi viene fornito un elenco di intervalli, come 11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124 e devi trovare quelli che non sono ID validi.\nPer la prima parte, gli ID non validi sono quelli che si ripetono esattamente due volte, come 11 o 123123. Per la seconda parte, possono ripetersi due o più volte, come 131313.\n(animazione per gentile concessione di https://www.reddit.com/user/Boojum/)\nDato che il problema riguarda interamente il filtraggio di un elenco, ho optato per uno stile funzionale. Il linguaggio di programmazione D offre ottime funzionalità in questo senso:\nbool isInvalidId1(string id) { auto mid = id.length / 2; return id.length \u0026gt; 0 \u0026amp;\u0026amp; id.length % 2 == 0 \u0026amp;\u0026amp; id[0 .. mid] == id[mid .. $]; } bool isInvalidId2(string id) { auto m = id.length; foreach (k; 2 .. m + 1) { if (m % k == 0) { auto firstToken = id[0 .. m / k]; if (firstToken.replicate(k).equal(id)) return true; } } return false; } void main() { auto ranges = stdin.readln().strip.split(\u0026#39;,\u0026#39;).map!(pair =\u0026gt; pair.split(\u0026#39;-\u0026#39;).map!(to!long)); auto numbers = ranges.map!(r =\u0026gt; iota(r[0], r[1] + 1).map!(to!string)).joiner; writeln(numbers.filter!isInvalidId1.map!(to!long).sum); writeln(numbers.filter!isInvalidId2.map!(to!long).sum); } A proposito, questo problema è interessante perché può essere affrontato in molti modi diversi: confronto di stringhe, espressioni regolari e approccio puramente aritmetico. Possiamo anche notare che l\u0026rsquo;intervallo di input è limitato, ad esempio i numeri più grandi hanno dieci cifre. Ciò significa che anche i modi possibili in cui un pattern di cifre può \u0026ldquo;ripetersi\u0026rdquo; sono limitati.\n🔋 Giorno 3 : Il voltaggio delle batterie Dobbiamo raggiungere i piani inferiori, ma sfortunatamente gli ascensori sono senza corrente. Il problema di oggi consiste nel collegare tra loro alcune batterie per ottenere il massimo \u0026ldquo;Joltage\u0026rdquo; possibile. Abbiamo quindi quattro pacchi batteria, qui rappresentati dalle seguenti righe:\n987654321111111 811111111111119 234234234234278 818181911112111 Per ogni pacco, vuoi trovare il numero più grande che puoi ottenere collegando due batterie; ad esempio, nella prima riga, il 9 e l\u0026rsquo;8 danno 98.\n(animazione per gentile concessione di https://www.reddit.com/user/danmaps/)\nNella seconda parte sarà necessario collegare 12 batterie.\nEcco la soluzione di oggi in Nim (pubblico qui solo la prima parte, puoi trovare la seconda parte sul mio repository)\ntemplate benchmark(code: untyped) = block: let t0 = getMonoTime() code let elapsed = getMonoTime() - t0 echo \u0026#34;Time \u0026#34;, elapsed.inMilliseconds(), \u0026#34; ms\u0026#34; proc part1(data: seq[int]): int = for i in 0 ..\u0026lt; data.len - 1: let currentVal = 10 * data[i] + data[i + 1 .. ^1].max result = max(result, currentVal) var input: seq[seq[int]] for line in stdin.lines: input.add line.map(proc(c: char): int = parseInt($c)) benchmark: echo \u0026#34;Part 1: \u0026#34;, input.map(part1).sum L\u0026rsquo;algoritmo è lineare: per ogni cifra, la si accoppia con la cifra successiva più grande. La coppia così formata è una candidata a dventare il nuovo valore massimo.\nUn paio di osservazioni sul linguaggio Nim, che a mio parere ha un enorme potenziale:\nMi piace la facilità con cui si possono scrivere i template (vedi la funzione benchmark in alto) e come si integrano in modo trasparente con la sintassi del linguaggio. La speciale variabile result è molto comoda per qualsiasi calcolo e viene restituita automaticamente alla fine della funzione. Il programma viene compilato in un binario nativo estremamente veloce: utilizzando l\u0026rsquo;input reale 100x200, il programma restituisce il valore corretto in circa 2 millisecondi. 🧻 Giorno 4 : Rotoli di carta Procedendo verso la base sotterranea, incontriamo il reparto stampa degli Elfi, dove viene stampata la famosa lista dei \u0026ldquo;buoni e cattivi\u0026rdquo;. I carrelli elevatori sono impegnatissimi con enormi rotoli di carta @, così decidiamo di dare una mano.\n..@@.@@@@. @@@.@.@.@@ @@@@@.@.@@ @.@@@@..@. @@.@@@@.@@ .@@@@@@@.@ .@.@.@.@@@ @.@@@.@@@@ .@@@@@@@@. @.@.@@@.@. Si scopre che possiamo spostare solo i rotoli che hanno meno di 4 elementi adiacenti!\n(animazione per gentile concessione di https://www.reddit.com/user/wimglenn/)\nPer questo esercizio ho voluto testare Zig, quindi temo che il codice sia troppo lungo per essere inserito qui. Se ti interessa, dai un\u0026rsquo;occhiata al repository!\n🥐 Giorno 5 : La mensa Dopo aver sfondato la parete (!) con un carrello elevatore, scopriamo che dietro c\u0026rsquo;è una mensa. Il compito di oggi consiste nel trovare gli ID degli ingredienti freschi in mezzo a quelli avariati, dato un elenco di intervalli e l\u0026rsquo;ID da verificare:\n3-5 10-14 16-20 12-18 1 5 8 11 17 32 La metà superiore dell\u0026rsquo;input rappresenta gli intervalli freschi, mentre quella inferiore contiene gli ingredienti. Ad esempio, 1 è avariato perché non è contenuto in alcun intervallo, mentre 11 appartiene all\u0026rsquo;intervallo 10-14, quindi è fresco.\nQuesto problema è molto interessante perché può essere risolto in molti modi diversi, esplorando concetti di efficienza e algoritmi della teoria degli insiemi. Ho adottato un approccio funzionale, utilizzando Elixir come linguaggio. Il codice completo si trova sul repository.\nNella prima parte, contiamo semplicemente quanti ingredienti rientrano nei nostri \u0026ldquo;intervalli freschi\u0026rdquo;.\ndef part1(fresh, ingredients) do Enum.count(ingredients, fn ingredient -\u0026gt; Enum.any?(fresh, \u0026amp;(ingredient in \u0026amp;1)) end) end Per risolvere la seconda parte, dobbiamo fondamentalmente \u0026ldquo;unire\u0026rdquo; tutti gli intervalli e contare tutti gli ID che vi rientrano. Questo è un lavoro perfetto per l\u0026rsquo;operatore di pipe (|\u0026gt;)!\ndef part2(fresh) do fresh |\u0026gt; Enum.sort() |\u0026gt; merge_ranges() |\u0026gt; Enum.map(\u0026amp;Range.size/1) |\u0026gt; Enum.sum() end (animazione per gentile concessione di https://www.reddit.com/user/Ok-Curve902/)\nLa logica principale è eseguita dalla funzione merge_ranges(). Vediamola:\ndefp merge_ranges([]), do: [] defp merge_ranges([r1, r2 | rest]) when r2.first \u0026lt;= r1.last + 1 do merged_range = r1.first..max(r1.last, r2.last) merge_ranges([merged_range | rest]) end defp merge_ranges([head | tail]) do [head | merge_ranges(tail)] end Si tratta di una funzione ricorsiva che sfrutta il potente pattern matching di Elixir:\nCaso base: una lista vuota restituisce semplicemente una lista vuota. Caso di unione (merge): quando gli intervalli si sovrappongono, crea un merged_range. Questo nuovo intervallo inizia all\u0026rsquo;inizio del primo intervallo (r1.first) e termina nel punto finale più grande tra i due (max(r1.last, r2.last)). Richiama poi nuovamente merge_ranges. Cosa fondamentale, passa una nuova lista in cui r1 e r2 sono stati sostituiti dal singolo merged_range. Ciò consente di verificare se l\u0026rsquo;intervallo appena formato si sovrappone a quello successivo nell\u0026rsquo;elenco (rest). Caso di mancata unione (no merge): poiché head non si sovrappone all\u0026rsquo;intervallo successivo, viene considerato per il momento come un intervallo finale e completo. Possiamo quindi inserirlo all\u0026rsquo;inizio della nostra lista dei risultati. La funzione chiama poi ricorsivamente merge_ranges sulla coda (tail) della lista per proseguire il processo con tutti gli intervalli successivi. Il risultato di tale chiamata ricorsiva viene accodato a head. Combinando queste tre clausole, la funzione scorre in modo elegante l\u0026rsquo;elenco ordinato, unendo gli intervalli man mano che procede, fino a elaborare l\u0026rsquo;intera lista.\n(animazione per gentile concessione di https://www.reddit.com/user/Just-Routine-5505/)\n🎅 Note e riferimenti Raccolgo qui tutti i collegamenti, i riferimenti o gli elementi correlati all\u0026rsquo;AoC25:\n(attenzione: potrebbero essere presenti offerte commerciali)\nAdvent of DevOps: https://sadservers.com/advent Advent of Cyber: https://tryhackme.com/adventofcyber25 AoC in Kotlin: https://blog.jetbrains.com/kotlin/2025/11/advent-of-code-in-kotlin-2025/ AoC Subreddit: https://www.reddit.com/r/adventofcode/ Perl Advent Calendar: https://perladvent.org/2025/ (Day 20 is very special!) Visualizzazione del Giorno 6 a cura di https://www.reddit.com/user/Ok-Curve902/: Giorno 7 risolto su un vero albero di Natale! Per gentile concessione di https://www.reddit.com/user/EverybodyCodes/\n","permalink":"https://ilmanzo.github.io/it/post/advent_of_code_2025/","summary":"\u003ch2 id=\"-intro\"\u003e🎄 Intro\u003c/h2\u003e\n\u003cp\u003eÈ dicembre, il periodo dell\u0026rsquo;anno più bello per i programmatori. Ma effettuando l\u0026rsquo;accesso all\u0026rsquo;\u003ca href=\"https://adventofcode.com/2025\"\u003eAdvent of Code (AoC) 2025\u003c/a\u003e, potresti notare che l\u0026rsquo;atmosfera è leggermente diversa. Abbiamo superato un decennio dello straordinario lavoro di Eric Wastl, e con questo traguardo arriva un importante cambiamento nella tradizione.\u003c/p\u003e\n\u003cp\u003ePrima di tuffarmi nelle soluzioni, voglio prendermi un momento per riflettere sullo stato dell\u0026rsquo;AoC di quest\u0026rsquo;anno, sui cambiamenti che stiamo vedendo e sul perché — nonostante tutto — continuiamo a tornare davanti al terminale.\u003c/p\u003e","title":"Advent of Code 2025: i diari"},{"content":"Introduzione Ottobre è giunto al termine e voglio condividere un breve resoconto della mia partecipazione ad alcune conferenze. Prima di tutto, un grande ringraziamento agli organizzatori che dedicano le loro energie a queste iniziative, e a SUSE per aver supportato la mia partecipazione!\nModena, 4 ott 2025: GDG DevFest 2025 Sede antica e storica: la location era nel centro della città: una vera e propria chiesa e un teatro Sono riuscito ad esserci solo il sabato I ragazzi di modena.rb e lo stand Linux! talks: Gabriele Santomaggio Creare un Linux container da zero Andrea Carratta Infrastructure as Code Soumaya Erradi Mastering Git Moreno Razzoli Containerizza la tua App e distribuisci senza pensieri Valeria Salis M71A Manifesto italiano per l\u0026rsquo;uguaglianza delle ragazze e delle donne nella tecnologia Non avevo mai visto biglietti da visita a forma di carte di Magic, e sono fantastici!\nFirenze, 5-7 ott 2025: GoLab A proposito di location straordinarie, la decima edizione di GoLab si è tenuta a Firenze.\nEntrambe le giornate sono state letteralmente dense di talk, ho cercato di seguirne il più possibile perché erano tutti davvero interessanti. Tra tutti, ho preferito i talk più low-level e di approfondimento tecnico (deep-dive), e non sono rimasto deluso dalla mia scelta.\nGiorno uno Raphael Amorim Unchaining Charm: Making Developer Tools Speak Every Language Alex Rios Tug-of-Code: The battle for efficient iteration in Go Lovro Mažgon Building a Plugin System with WebAssembly Jesús Espino Deep dive into the select statement Erik Pellizzon Bypassing the Linux net stack with Go Ron Evans That Machine Always Lies Giorno due Ricardo Gomes da Silva Becoming a Game Developer 25 Years Too Late Raiza Claudino marter Locks: Diving into Go 1.24’s Mutex Spin Optimisation Naoki Kuroda Weak References in Go 1.24: Memory Management Superpowers (Lightning talks) Divya Rani SIMD Support via Go Assembly Sam Burns Post-Quantum Cryptography in Go: The Arrival of the crypto/mlkem Package Bill Kennedy Go’s Trace Tooling and Concurrency Chi non ama i fantastici adesivi?\nVenezia, 11 ott 2025: GDG DevFest 2025 L\u0026rsquo;Università Ca\u0026rsquo; Foscari ha un design futuristico, non c\u0026rsquo;è da stupirsi che la chiamino \u0026lsquo;il Sandcrawler di Java\u0026rsquo;\nIl talk che ho proposto è stato accettato qui, quindi sono andato a parlare di green computing e di come misurare il consumo energetico senza apparecchiature speciali. Ho anche colto l\u0026rsquo;occasione per distribuire dei fantastici gadget (swag) forniti dall\u0026rsquo;azienda!\n(Sì, sono persino riuscito a scrivere male il mio nome 😑)\nBrescia, 25 ott 2025: Linux Day Il Linux Day è uno dei miei preferiti da sempre, dal 2001 ogni anno mi piace partecipare e condividere la conoscenza e la passione per Linux e l\u0026rsquo;Open Source. In questa edizione ho avuto il piacere di parlare in una sala affollata, con molti giovani e anche utenti Linux veterani.\nHo tenuto due talk, uno la mattina sulle basi del kernel Linux (mostrando l\u0026rsquo;anatomia di una syscall) e un \u0026ldquo;workshop\u0026rdquo; più ampio su Kubernetes.\nAttrezzatura ottica e software molto interessanti per l\u0026rsquo;astrofotografia\nHo anche portato a casa questo simpatico gadget che riassume bene lo spirito dell\u0026rsquo;Open Source: costruisci il tuo pinguino! \u0026#x1f604;\nRitorno a casa Sono un grande fan delle piccole conferenze locali. Sono molto più semplici: sono economiche, vicine a casa e non ti portano via un\u0026rsquo;intera settimana. Anche il networking è molto più prezioso; ti metti in contatto con persone che lavorano e vivono effettivamente nella tua zona.\nInoltre, le dimensioni ridotte significano meno caos, quindi puoi letteralmente avvicinarti e fare una vera conversazione con i relatori. E se stai pensando di salire tu stesso sul palco, questi eventi sono il trampolino di lancio perfetto. È un modo a bassa pressione per fare esperienza e farsi conoscere. Ve li consiglio caldamente.\n","permalink":"https://ilmanzo.github.io/it/post/october_2025_conferences/","summary":"\u003ch1 id=\"introduzione\"\u003eIntroduzione\u003c/h1\u003e\n\u003cp\u003eOttobre è giunto al termine e voglio condividere un breve resoconto della mia partecipazione ad alcune conferenze. Prima di tutto, un grande ringraziamento agli organizzatori che dedicano le loro energie a queste iniziative, e a SUSE per aver supportato la mia partecipazione!\u003c/p\u003e\n\u003ch2 id=\"modena-4-ott-2025-gdg-devfest-2025\"\u003eModena, 4 ott 2025: GDG DevFest 2025\u003c/h2\u003e\n\u003cp\u003e\u003cimg alt=\"modena1\" loading=\"lazy\" src=\"/img/octoberconf_2025/modena1.jpg\"\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eSede antica e storica: la location era nel centro della città: una vera e propria chiesa e un teatro\u003c/li\u003e\n\u003cli\u003eSono riuscito ad esserci solo il sabato\u003c/li\u003e\n\u003cli\u003eI ragazzi di \u003ccode\u003emodena.rb\u003c/code\u003e e lo stand Linux!\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"modena2\" loading=\"lazy\" src=\"/img/octoberconf_2025/modena2.jpg\"\u003e\u003c/p\u003e","title":"Resoconto delle conferenze di ottobre 2025"},{"content":"🧹 Il mio disordinato sistema Linux Con il passare degli anni, il mio sistema Linux inizia a diventare un po\u0026rsquo;\u0026hellip; disordinato.\nLa mia directory ~/Downloads è una vera e propria discarica digitale. È un accumulo di ISO, script di test e quelle enormi immagini di macchine virtuali da svariati gigabyte che devo testare una volta e poi dimentico. Aggiungete a questo ~/Pictures/Screenshots, che trabocca di migliaia di catture rapide che non guarderò mai più.\nLo spazio su disco oggi costa poco, ma mi piace avere le cose in ordine e pulite. Potrei procedere a una pulizia manuale\u0026hellip; Ma chi si ricorda di farlo?\nCosì ho configurato una rapida soluzione automatizzata; si è rivelata un\u0026rsquo;ottima occasione per studiare i timer di systemd!\nLa mia regola è semplice: se non ho effettuato l\u0026rsquo;accesso (o \u0026ldquo;aperto\u0026rdquo;) un file in uno di questi due percorsi per 30 giorni, lo considero spazzatura e voglio che venga rimosso.\n(vi prego di perdonare l\u0026rsquo;immancabile e stucchevole immagine generata dall\u0026rsquo;IA)\n🗂️ Il tuo personale spazzino systemd Dobbiamo solo creare due semplici file di testo:\nUn file .service: dice allo spazzino cosa fare. Un file .timer: dice allo spazzino quando farlo. Prima di tutto, ci serve un posto dove posizionare i nostri nuovi file. Systemd cerca i file dell\u0026rsquo;utente in ~/.config/systemd/user/.\nmkdir -p ~/.config/systemd/user/ Il primo file definirà il comando di pulizia. Create un nuovo file chiamato ~/.config/systemd/user/cleanup-files.service:\n[Unit] Description=Clean up old files in Download and Screenshots [Service] Type=oneshot ExecStart=/usr/bin/find %h/Downloads %h/Pictures/Screenshots -type f -atime +29 -delete Nice=19 IOSchedulingClass=idle Analizziamolo nel dettaglio:\nType=oneshot: significa semplicemente che esegue un singolo comando e si ferma. ExecStart=...: questo è il comando magico! %h/Downloads %h/Pictures/Screenshots: %h è la scorciatoia speciale di systemd per la vostra home directory. Diciamo a find di cercare in entrambi i percorsi. -type f: trova solo file, non directory vuote. -atime +29: trova i file il cui ultimo accesso risale a più di 29 giorni fa (ovvero, 30 o più giorni). -delete: sì, li elimina. Nice=19 e IOSchedulingClass=idle: queste sono buone maniere per il sistema. Gli dicono di eseguire questo comando con la priorità più bassa possibile, in modo da non rallentare mai il vostro lavoro reale. nota: se il filesystem della vostra /home/ è montato con le opzioni noatime o relatime per migliorare le prestazioni, potreste ottenere risultati più prevedibili utilizzando invece mtime (tempo di modifica). In questo modo verranno eliminati i file che non subiscono modifiche da 30 giorni.\n⚠️ Avviso di sicurezza! Prima di lasciarlo girare, fate una prova! Copiate il comando nel terminale, ma rimuovete la parte -delete.\n# QUESTO ELENCHERÀ SOLO I FILE. NON ELIMINERÀ NULLA. find ~/Downloads ~/Pictures/Screenshots -type f -atime +29 Ora creiamo la pianificazione.\nCreate un nuovo file chiamato ~/.config/systemd/user/cleanup-files.timer:\n[Unit] Description=Run cleanup-files.service daily [Timer] OnCalendar=daily Persistent=true [Install] WantedBy=timers.target Questo è semplice:\nOnCalendar=daily: esegue l\u0026rsquo;operazione una volta al giorno (solitamente intorno a mezzanotte). Persistent=true: questa è la parte migliore. Se il computer era spento a mezzanotte, eseguirà il comando non appena avviate il sistema ed effettuate l\u0026rsquo;accesso. ⏲️ Avviate il vostro timer! Avete creato lo spazzino; è ora di attivarlo. Dite a systemd di leggere i vostri nuovi file:\nsystemctl --user daemon-reload Abilitate e avviate il timer:\nsystemctl --user enable --now cleanup-files.timer Potete verificare che il timer sia attivo e in attesa eseguendo:\nsystemctl --user status cleanup-files.timer systemctl --user list-timers 🗑️ Passaggio bonus: pulizia dei pacchetti (per gli utenti openSUSE 🦎) La pulizia dei file è ottima, ma come utente Tumbleweed, il mio sistema riceve aggiornamenti costanti, che possono lasciare pacchetti \u0026ldquo;orfani\u0026rdquo; o non necessari — dipendenze che erano state installate per qualcosa ma che non sono più richieste. Per questo ho anche un comodo alias\nalias zclean=\u0026#39;zypper packages --orphaned \u0026amp;\u0026amp; zypper packages --unneeded\u0026#39; che mi mostra i pacchetti potenzialmente candidati, che posso poi rimuovere con le loro dipendenze:\nzypper rm -u \u0026lt;PACKAGENAME\u0026gt; Godetevi il vostro sistema più pulito e buon hacking!\n","permalink":"https://ilmanzo.github.io/it/post/my-linux-janitor/","summary":"\u003ch2 id=\"-il-mio-disordinato-sistema-linux\"\u003e🧹 Il mio disordinato sistema Linux\u003c/h2\u003e\n\u003cp\u003eCon il passare degli anni, il mio sistema Linux inizia a diventare un po\u0026rsquo;\u0026hellip; \u003cem\u003edisordinato\u003c/em\u003e.\u003c/p\u003e\n\u003cp\u003eLa mia directory \u003ccode\u003e~/Downloads\u003c/code\u003e è una vera e propria discarica digitale. È un accumulo di ISO, script di test e quelle enormi immagini di macchine virtuali da svariati gigabyte che devo testare una volta e poi dimentico. Aggiungete a questo \u003ccode\u003e~/Pictures/Screenshots\u003c/code\u003e, che trabocca di migliaia di catture rapide che non guarderò mai più.\u003c/p\u003e","title":"Pulizia automatica dei file su Linux"},{"content":"😸 TL;DR Essendo pigro, ho creato uno strumento per eseguire immagini qcow2 a mia comodità. Ora supporta x86_64, aarch64, s390x e ppc64le. Sentitevi liberi di usarlo se lo trovate utile!\n📖 La storia Se avete mai digitato qemu-system-x86_64 nel vostro terminale, conoscete bene la sensazione. Una paura strisciante. Un sudore freddo. Il mal di testa da QEMU. È quella speciale emicrania riservata agli sviluppatori che sanno che passeranno i successivi dieci minuti a decifrare la cronologia della propria shell per ricordare quel flag magico per la rete.\nQuesta era la mia realtà quotidiana. Il mio lavoro richiede di testare software su un intero zoo di versioni di Linux attraverso architetture come x86_64, aarch64, s390x e ppc64le. Vivo di immagini qcow2, creandole e distruggendole continuamente. Ma ogni singolo avvio era una nuova avventura nel dimenticare i flag. Ho aggiunto -cpu host per le prestazioni? Mi sono ricordato di -device usb-tablet in modo che il mouse non sembri fare breakdance nell\u0026rsquo;angolo? 🤯\nDopo aver accidentalmente salvato per la centesima volta modifiche distruttive su un\u0026rsquo;immagine di base pulita perché avevo dimenticato -snapshot, ho avuto un momento di chiarezza. Un glorioso momento in cui ho ribaltato il tavolo. ╯°□°）╯︵ ┻━┻\nIl mio problema non era QEMU. Era ricordare le opzioni di QEMU. La soluzione era semplice: costruire uno strumento in modo da potermene dimenticare.\n🥾 Una cura per il comune raffreddore della riga di comando Con la crescita del progetto, sono cresciute anche le mie esigenze. Avevo bisogno di uno script wrapper, un compagno per l\u0026rsquo;avvio rapido che fosse robusto, portabile e facile da manutenere. La ricetta era chiara:\nUna singola pillola: un binario autonomo. Nessun effetto collaterale, nessuna dipendenza, nessun \u0026ldquo;assicurati di avere installato Python 3.9.x\u0026rdquo;.\nFacile da modificare: doveva essere abbastanza leggibile da evitare che il \u0026ldquo;Me del futuro\u0026rdquo; maledicesse il \u0026ldquo;Me del passato\u0026rdquo;.\nLo strumento giusto per il lavoro: abbastanza potente da gestire la complessità, ma abbastanza semplice per un piccolo progetto secondario.\nInizialmente ho scritto questo strumento in D, ma guardando al futuro del progetto, sapevo di aver bisogno di qualcos\u0026rsquo;altro. Così ho deciso di riscriverlo in Go. 🐹\nGo non era solo il ragazzo popolare del quartiere; per un progetto come questo, rappresentava l\u0026rsquo;evoluzione perfetta. Ha soddisfatto ognuno dei punti della mia lista dei desideri con pragmatica eleganza:\nSingolo eseguibile? ✅ Go compila in un binario nativo statico per impostazione predefinita. Il mio helper q2boot è un solo file, pronto all\u0026rsquo;uso su qualsiasi sistema Linux. Deployment? Risolto.\nAmore per Linux? ✅ La compilazione incrociata di Go è leggendaria. Compilare per aarch64, s390x e ppc64le da una singola macchina è banale, non un progetto che richiede un intero fine settimana.\nLeggibilità e potenza? ✅✅ È qui che Go eccelle per gli strumenti CLI. La sua sintassi è pulita, semplice e incredibilmente facile da imparare. La robusta libreria standard offre tutto ciò di cui si ha bisogno per file, comandi e rete. Inoltre, funzionalità come goroutine e canali, pur essendo forse eccessive per questo strumento oggi, mi danno spazio per crescere senza sovra-ingegnerizzazione. Ha tutta la potenza di cui ho bisogno senza complicazioni.\n💆 Headless per impostazione predefinita Ho iniziato a creare lo strumento q2boot, definendo una struttura VirtualMachine per contenere tutte le opzioni. Questo approccio \u0026ldquo;orientato agli oggetti\u0026rdquo; significava che la configurazione della mia macchina virtuale era nettamente separata dalla complessa logica di costruzione dei comandi QEMU. Niente più codice spaghetti! 🍝\nIl mio strumento q2boot è stato progettato attorno ai miei due flussi di lavoro principali:\n🤖 Modalità Headless (impostazione predefinita): serve per il mio lavoro quotidiano di test. Viene eseguita con -nographic e, cosa più importante, utilizza un -snapshot in modo che le mie immagini di base rimangano intatte. È veloce, pulita e usa-e-getta come un bicchiere di carta. Questa modalità è il motivo per cui ora riesco a dormire la notte.\n🧑‍💻 Modalità interattiva (-i per \u0026ldquo;ho bisogno di vedere!\u0026rdquo;): serve per gli interventi manuali. Quando devo effettivamente accedere, creare un utente o installare qualcosa, questa modalità apre una GUI, abilita un mouse che risponde ai comandi e disabilita gli snapshot in modo che le mie modifiche vengano salvate.\nPer renderlo ancora più semplice, q2boot crea automaticamente un file config.json in ~/.config/q2boot/ al primo avvio. Devo solo impostare CPU e RAM predefinite una sola volta, e ho finito. Per sempre.\n♻️ Il glorioso risultato Il mio flusso di lavoro è stato trasformato.\nPrima di q2boot (Il mal di testa):\n$ qemu-system-x86_64 -m 8G -cpu host \\ -enable-kvm -drive file=... aspetta, com\u0026#39;era di nuovo la sintassi per virtio? *apre Google* Dopo q2boot (La beatitudine headless):\n$ ./q2boot -d my-disk.qcow2 Tutto qui. Funziona e basta. Se devo effettuare prima della configurazione:\n$ ./q2boot -d my-disk.qcow2 -w Il mio mal di testa da QEMU è sparito, sostituito dal ronzio silenzioso di uno strumento che fa esattamente quello che voglio.\n💭 Considerazioni finali Anche se Go ha già vinto il concorso di popolarità, vale la pena ripetere perché sia perfetto per utilità pratiche a riga di comando.\nCompilazione ed esecuzione rapide: Go compila in modo incredibilmente veloce, rendendo il ciclo \u0026ldquo;modifica-compila-esegui\u0026rdquo; un piacere. Il binario risultante è performante e si avvia istantaneamente.\nLa semplicità è geniale: offre un insieme ridotto e mirato di funzionalità che sono incredibilmente efficaci per la creazione di software affidabile e manutenibile. Si passa il tempo a risolvere problemi, non a combattere contro il linguaggio.\nDeploy ovunque: la possibilità di effettuare la compilazione incrociata in un singolo binario privo di dipendenze è un superpotere. Condividere lo strumento con altri o distribuirlo su macchine diverse è semplicissimo.\nQuindi, la prossima volta che siete stanchi di lottare con le opzioni della riga di comando per i vostri strumenti di sviluppo, o se avete bisogno di un\u0026rsquo;utilità rapida, performante e facilmente distribuibile, fate un tentativo con Go. Potreste trovare il vostro compagno gopher personale. ✨ Ha sicuramente salvato la mia salute mentale dal Tetris di QEMU!\nIl repository del progetto è disponibile all\u0026rsquo;indirizzo https://github.com/ilmanzo/q2boot . I contributi sono i benvenuti!\n","permalink":"https://ilmanzo.github.io/it/post/qboot-qemu-helper/","summary":"\u003ch2 id=\"-tldr\"\u003e😸 TL;DR\u003c/h2\u003e\n\u003cp\u003eEssendo pigro, ho creato \u003ca href=\"https://github.com/ilmanzo/q2boot\"\u003euno strumento\u003c/a\u003e per eseguire immagini \u003ccode\u003eqcow2\u003c/code\u003e a mia comodità. Ora supporta x86_64, aarch64, s390x e ppc64le. Sentitevi liberi di usarlo se lo trovate utile!\u003c/p\u003e\n\u003ch2 id=\"-la-storia\"\u003e📖 La storia\u003c/h2\u003e\n\u003cp\u003eSe avete mai digitato \u003ccode\u003eqemu-system-x86_64\u003c/code\u003e nel vostro terminale, conoscete bene la sensazione. Una paura strisciante. Un sudore freddo. Il \u003cem\u003emal di testa\u003c/em\u003e da QEMU. È quella speciale emicrania riservata agli sviluppatori che sanno che passeranno i successivi dieci minuti a decifrare la cronologia della propria shell per ricordare quel flag magico per la rete.\u003c/p\u003e","title":"Dal mal di testa con QEMU alla modalità Headless"},{"content":"Esperienza Lavorativa 2022-oggi: QE Automation Engineer, SUSE\nSviluppo di test automatizzati e garanzia di qualità (QA) per il rilascio dei prodotti SUSE. Manutentore di alcuni pacchetti RPM. Contributore open source.\n2017-2022: cloud \u0026amp; automation senior sw engineer, Value Transformation Services\nLavoro su un\u0026rsquo;implementazione cloud ibrido completamente automatizzata. Consegna e gestione, tramite best practice DevOps, di una piattaforma personalizzata utilizzata dal cliente per richiedere soluzioni IaaS e PaaS.\n2013-2016: linux senior system engineer, Value Transformation Services\nGestione e introduzione dell\u0026rsquo;automazione completa su un parco di circa 18.000 server Linux multi-datacenter.\n2011-2013: linux system engineer, Unicredit\n2005-2011: sviluppatore di prodotti di remote banking e pagamenti remoti per il gruppo Unicredit\nLavoro su prodotti di remote banking, sia su tecnologie desktop che POS.\n2001-2005: amministratore di rete e sicurezza in Quercia Software spa\nAmministrazione di dispositivi LAN e WAN, sicurezza e implementazione di infrastruttura VoIP.\n2000-2001: sysadmin, network administrator presso un Internet Service Provider locale, Netbusiness spa\nGestione di servizi ISP, rete e operazioni.\n1999-2000: sviluppatore, DBA Oracle e sysadmin VAX/VMS\nImplementazione di personalizzazioni e migrazioni sul software di gestione bibliotecaria più grande del mondo.\nAttivo nel settore IT dal 1993; primo server Linux installato nel 1995.\nCertificazioni 2022: AZ-204 Microsoft Azure Developer Associate 2021: AZ-900 Microsoft Azure Fundamentals 2021: Red Hat Certified Engineer (certificato numero 140-219-585) 2018: Red Hat Certified Specialist in Ansible Automation (certificato numero 140-219-585) 2018: Certified Openstack Administrator Linux Foundation Certificate id COA-1800-001215-0100 2018: SUSE Certified Engineer in Enterprise Linux - SLES 12 (certificato id 10170115) 2017: SUSE Certified Administrator in Enterprise Linux - SLES 12 (certificato id 10170115) 2016: RHCVA: RedHat Certified Virtualization Administrator (certificato numero 140-219-585) 2015: RHCE: RedHat Certified Engineer in Red Hat Enterprise Linux 7 (certificato numero 140-219-585) 2015: RedHat Certified System Administrator in Red Hat Enterprise Linux 7 (certificato numero 140-219-585) 2014: RedHat Certified System Administrator in Red Hat Openstack (certificato numero 140-219-585) 2012: Certificazione Novell CLA con punteggio 791/800 Pubblicazioni 2004: Linux\u0026amp;C, articoli sul sottosistema PAM (Pluggable Authentication Modules) 2005: Linux\u0026amp;C, articoli su Unicode e la sua implementazione in Linux 2006: Linux\u0026amp;C, serie di articoli su SNMP, MRTG e monitoraggio di rete 2008: Linux\u0026amp;C, articoli sulle gnome desklet 2010: Linux\u0026amp;C, wiki MoinMoin, come estenderlo con Python 2010: Linux\u0026amp;C, articoli su imagemagick (grafica da riga di comando) Progetti Personali BinaryCoverage, un metodo innovativo per misurare la copertura dei test di integrazione Q2Boot, un lanciatore automatico di macchine virtuali multi-architettura DlangKoans e crystal-koans, suite di test didattiche per imparare i linguaggi in modo semplice Formazione e Corsi 2023: Schrödinger Session: Learning Rust, one step at the time dalla community Schrödinger Hat 2022: Ultimate Rust Crash Course, udemy 2021: Learn Rust by Building Real Application, udemy 2021: Building Modern Python Applications on AWS, Coursera 2021: Data Center Automation with vRealize Orchestrator and vSphere PowerCLI 2021: Red Hat Linux Automation with Ansible (RH294) 2019: DevOps Engineering on AWS 2018: Systems Operations on AWS 2018: Developing on AWS 2018: Red Hat Automation with Ansible (DO407) 2017: Red Hat JBoss Application Administration I (JB248) 2017: Suse Linux 12 academy, SUSE OpenStack Cloud Academy 2016: Red Hat Enterprise Virtualization (RH318) 2015: RHCE Certification lab con esami RHCSA e RHCE (RH300) 2014: Red Hat OpenStack Administration con esame di specializzazione (CL211) 2011: RH442 Red Hat Enterprise Performance Tuning 2011: RH346 Red Hat Enterprise Clustering and Storage Management 2010: MIE3520 Programmazione Win32 presso Mondadori Informatica 2010: MIE3510 Programmazione in ANSI C++ presso Mondadori Informatica 2009: Java Fundamentals, il linguaggio di programmazione Java presso Overnet Education 2008: Cisco BCMSN - Building Cisco Multilayer Switched Networks 2007: Microsoft MOC 2159 Deploying and Managing Microsoft Internet Security and Acceleration (ISA) Server 2007: Microsoft MOC 2150 Designing a Secure Microsoft Windows 2000 Network 2006: Microsoft MOC 2153 Implementing a Microsoft Windows 2000 Network Infrastructure 2006: Microsoft MOC 2087 Implementing Windows 2000 clustering 2005: ES-331 Sun Enterprise Cluster Administration Veritas Volume Altre Esperienze 2025: relatore al Linux Day Brescia 2025: relatore al GdG DevFest Venezia 2025: relatore al Tech Community Day di Coding Bunker \u0026amp; GrUSP 2024: relatore al Linux Day Brescia 2024: relatore al Linux Day Spring Edition @Mantova 2023: relatore al Google Developer Group Trento DevFest 2023: diventato Ambassador per il linguaggio di programmazione Crystal 2022: diventato manutentore di pacchetti rpm openSUSE su Open Build Service 2022: workshop sul testing di applicazioni web per un\u0026rsquo;azienda privata 2021: formazione C# per studenti in tirocinio presso un\u0026rsquo;azienda privata 2021: webinar sulla sicurezza dei server IoT per Verona FabLab 2020: corso di formazione sulla crittografia / sicurezza (blockchain) per Verona FabLab 2019: corsi di formazione Linux e Python 2018: talk per FEVR sui chatbot e il linguaggio di programmazione Go. Slide collegate qui 2017: corso di formazione Linux presso FabLab Verona 2016: inizio contribuzione a progetti open source, vedi profilo GitHub 2016: corso di formazione Python presso FabLab Verona 2016: corso di formazione Linux presso FabLab Verona Varie Presentazioni (professionali): https://ilmanzo.github.io/suse_presentations ","permalink":"https://ilmanzo.github.io/it/curriculum/","summary":"\u003ch2 id=\"esperienza-lavorativa\"\u003eEsperienza Lavorativa\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\n\u003cp\u003e2022-oggi: \u003cstrong\u003eQE Automation Engineer\u003c/strong\u003e, \u003ca href=\"https://www.suse.com/\"\u003eSUSE\u003c/a\u003e\u003cbr\u003e\nSviluppo di test automatizzati e garanzia di qualità (QA) per il rilascio dei prodotti SUSE. Manutentore di alcuni pacchetti RPM. Contributore open source.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e2017-2022: \u003cstrong\u003ecloud \u0026amp; automation senior sw engineer\u003c/strong\u003e, \u003ca href=\"https://www.v-tservices.com/\"\u003eValue Transformation Services\u003c/a\u003e\u003cbr\u003e\nLavoro su un\u0026rsquo;implementazione cloud ibrido completamente automatizzata. Consegna e gestione, tramite best practice DevOps, di una piattaforma personalizzata utilizzata dal cliente per richiedere soluzioni IaaS e PaaS.\u003c/p\u003e\n\u003c/li\u003e\n\u003cli\u003e\n\u003cp\u003e2013-2016: \u003cstrong\u003elinux senior system engineer\u003c/strong\u003e, \u003ca href=\"https://www.v-tservices.com/\"\u003eValue Transformation Services\u003c/a\u003e\u003cbr\u003e\nGestione e introduzione dell\u0026rsquo;automazione completa su un parco di circa 18.000 server Linux multi-datacenter.\u003c/p\u003e","title":"il mio curriculum"},{"content":"Introduzione Giugno è quasi al termine e voglio condividere un breve resoconto della mia partecipazione ad alcune conferenze. Prima di tutto, un grande grazie agli organizzatori che dedicano le loro energie a queste iniziative.\nMilano, 20 giugno 2025: Tech Community Day Il Community Day è un evento non-profit di un giorno dedicato alle community tecnologiche italiane. La sua missione è creare uno spazio di condivisione, apprendimento e networking aperto a tutti coloro che lavorano e sono appassionati di tecnologia.\nOrganizzato in collaborazione tra Coding Bunker, GrUSP e Fondazione Brodolini, l\u0026rsquo;evento mira a rafforzare i legami tra le diverse community e a promuovere la collaborazione come motore di innovazione.\nIn questa occasione ho avuto l\u0026rsquo;onore di tenere un intervento sulla vita di un maintainer e su come chiunque nella community possa contribuire!\nEcco il video riassuntivo e alcune foto:\nMilano, vecchia e nuova\nGadget (swag) e caramelle!\nSono io!\nReggio Emilia, 27 giugno 2025: Working Software Per questo evento ho rimediato un biglietto all\u0026rsquo;ultimo minuto, dopo l\u0026rsquo;invito di alcuni amici sviluppatori. Il focus dell\u0026rsquo;evento è su sviluppo e testing agile, e su tutte le persone che si sporcano le mani costruendo prodotti che funzionano, rendendo felici gli utenti e profittevoli le aziende.\nIl sito web è davvero ben fatto, e la location fisica trasmetteva ottime vibrazioni: un complesso industriale dismesso e ristrutturato, che ospita spazi di coworking e l\u0026rsquo;università locale. I talk erano di alto livello, alternati a workshop molto interessanti in un programma (schedule) serrato. Le pause caffè e pranzo sono state preziose occasioni di networking, come sempre in questo tipo di eventi.\nArchitettura postmoderna\nSala conferenze accogliente\nGabriele Santomaggio, sviluppatore core di RabbitMQ\nPranzo e altre paperelle!\nRitorno a casa Mi piace partecipare a piccole conferenze locali per molte ragioni:\nTempi e costi di viaggio ridotti: spesso si svolgono in una sola giornata e le quote di iscrizione sono solitamente più basse. Connessioni più profonde con persone \u0026ldquo;vicine\u0026rdquo;, che parlano la stessa lingua madre e lavorano nello stesso territorio. Anche il networking è più rilevante. Le dimensioni gestibili comportano meno caos e una navigazione più semplice, offrendo più tempo per assistere ai talk e parlare con gli ospiti e i relatori, che sono anche più accessibili rispetto alle grandi conferenze. Per gli aspiranti relatori, una conferenza locale può anche essere un ottimo trampolino di lancio per acquisire esperienza e fiducia prima di puntare a palchi più grandi. La barriera d\u0026rsquo;ingresso per diventare relatore è spesso più bassa, quindi le consiglio caldamente.\n","permalink":"https://ilmanzo.github.io/it/post/june_2025_conferences/","summary":"\u003ch1 id=\"introduzione\"\u003eIntroduzione\u003c/h1\u003e\n\u003cp\u003eGiugno è quasi al termine e voglio condividere un breve resoconto della mia partecipazione ad alcune conferenze. Prima di tutto, un grande grazie agli organizzatori che dedicano le loro energie a queste iniziative.\u003c/p\u003e\n\u003ch2 id=\"milano-20-giugno-2025-tech-community-day\"\u003eMilano, 20 giugno 2025: Tech Community Day\u003c/h2\u003e\n\u003cp\u003eIl \u003ca href=\"https://www.techcommunityday.it/\"\u003eCommunity Day\u003c/a\u003e è un evento non-profit di un giorno dedicato alle community tecnologiche italiane. La sua missione è creare uno spazio di condivisione, apprendimento e networking aperto a tutti coloro che lavorano e sono appassionati di tecnologia.\u003c/p\u003e","title":"Resoconto delle conferenze di giugno 2025"},{"content":"▶️ Intro: Let Me Be Nel post precedente abbiamo continuato il nostro viaggio affrontando uno scenario più complesso, utilizzando un mix di gdb e valgrind per tracciare l\u0026rsquo;esecuzione di tutte le funzioni all\u0026rsquo;interno di un dato binario.\nQuesta volta reggetevi forte perché aumenteremo notevolmente la complessità. Ci immergeremo nell\u0026rsquo;analisi di basso livello ed esploreremo come utilizzare Intel PIN, un potente framework di strumentazione dinamica per manipolare e ispezionare il codice eseguibile a runtime.\n(Foto di FURQAN KHURSHID)\nIniziamo con un programma in C elementare, il cui comportamento varia a seconda degli argomenti passati sulla riga di comando:\n🎯 Il target: Gonna Make You Sweat #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int add(int a, int b) { return a+b; } int mul(int a, int b) { return a*b; } int main(int argc, char **argv) { if (argc \u0026lt; 3) { // must input 2 number args fprintf(stderr, \u0026#34;input 2 numbers for calc add or mul.\\n\u0026#34;); fprintf(stderr, \u0026#34;Usage) ./a.out 1 2\\n\u0026#34;); return -1; } int a = atoi(argv[1]); int b = atoi(argv[2]); if (a \u0026lt; b) { printf(\u0026#34;the answer is a + b = %d\\n\u0026#34;, add(a, b)); } else { printf(\u0026#34;the answer is a * b = %d\\n\u0026#34;, mul(a, b)); } return 0; } Compiliamolo, quindi, per usarlo come nostro banco di prova:\n$ cc -g -gdwarf-4 main.c -o cov_sample $ example/cov_sample 7 3 the answer is a * b = 21 $ example/cov_sample 2 5 the answer is a + b = 7 Compiliamo con il flag -g per incorporare le informazioni di debug direttamente nell\u0026rsquo;eseguibile. Anche se questo non è strettamente necessario — Pin può funzionare anche con file di simboli di debug esterni — semplifica il nostro esempio.\n📌 Uno strumento di tracciamento: Surrender Intel PIN è un framework di strumentazione binaria dinamica per le architetture dei set di istruzioni IA-32 e x86-64 che consente la creazione di strumenti di analisi dinamica dei programmi. Pin è fornito e supportato da Intel, gratuitamente per qualsiasi tipo di utilizzo, secondo i termini della Intel Simplified Software License (ISSL). Tutto il codice sorgente contenuto nel kit di Pin, compresi script, codice di esempio e header, è disciplinato dalla licenza MIT.\nPin consente a uno strumento di inserire codice arbitrario (scritto in C o C++) in punti arbitrari dell\u0026rsquo;eseguibile. Il codice viene aggiunto dinamicamente mentre l\u0026rsquo;eseguibile è in esecuzione. Ciò rende anche possibile collegare Pin a un processo già in esecuzione.\nPin fornisce una ricca API che astrae le peculiarità del set di istruzioni sottostante e consente di passare al codice iniettato informazioni di contesto come il contenuto dei registri sotto forma di parametri. Pin salva e ripristina automaticamente i registri sovrascritti dal codice iniettato, in modo che l\u0026rsquo;applicazione continui a funzionare. È inoltre disponibile un accesso limitato ai simboli e alle informazioni di debug.\nSenza ulteriori indugi, vediamo come si presenta un pintool:\n/* FuncTracer.cpp */ #include \u0026#34;pin.H\u0026#34; #include \u0026lt;iostream\u0026gt; // Questa funzione viene chiamata prima di ogni funzione nell\u0026#39;applicazione strumentata. // Registra l\u0026#39;ID del processo, il nome dell\u0026#39;immagine e il nome della funzione. VOID log_function_call(const char *img_name, const char *func_name) { // ... } // Pin chiama questa funzione per ogni immagine caricata nello spazio di indirizzamento del processo. // Un\u0026#39;immagine è un eseguibile o una libreria condivisa. VOID ImageLoad(IMG img, VOID *v) { // Iteriamo attraverso tutte le routine (funzioni) nell\u0026#39;immagine. for (SEC sec = IMG_SecHead(img); SEC_Valid(sec); sec = SEC_Next(sec)) { for (RTN rtn = SEC_RtnHead(sec); RTN_Valid(rtn); rtn = RTN_Next(rtn)) { std::stringstream ss; RTN_Open(rtn); ss \u0026lt;\u0026lt; \u0026#34;[Image:\u0026#34; \u0026lt;\u0026lt; IMG_Name(img) \u0026lt;\u0026lt; \u0026#34;] [Function:\u0026#34; \u0026lt;\u0026lt; RTN_Name(rtn) \u0026lt;\u0026lt; \u0026#34;]\\n\u0026#34; ; LOG(ss.str()); // Per ogni routine, inseriamo una chiamata alla nostra funzione di analisi `log_function_call`. RTN_InsertCall(rtn, IPOINT_BEFORE, (AFUNPTR)log_function_call, IARG_PTR, IMG_Name(img).c_str(), IARG_PTR, RTN_Name(rtn).c_str(), IARG_END); RTN_Close(rtn); } } } int main(int argc, char *argv[]) { PIN_InitSymbols(); if (PIN_Init(argc, argv)) { std::cerr \u0026lt;\u0026lt; \u0026#34;PIN_Init failed\u0026#34; \u0026lt;\u0026lt; std::endl; return 1; } // Registra la funzione da chiamare per ogni immagine caricata. IMG_AddInstrumentFunction(ImageLoad, 0); PIN_StartProgram(); return 0; } (questa è una versione ridotta, il programma completo è disponibile sul mio repository). Un grande ringraziamento a @simotin13 per aver fornito un prezioso punto di partenza!\nDiamo un\u0026rsquo;occhiata alle istruzioni per compilare questo programma in una libreria .so condivisa, seguendo la documentazione; se siete pigri o impazienti, troverete un comodo script build.sh e dei Makefile per compilare e collegare tutto correttamente.\n💌 Cosa viene registrato? Ora possiamo eseguire pin, passandogli il nostro plugin, ed eseguire il target, in cui verrà iniettato il nostro codice di strumentazione.\nexport PIN_ROOT = \u0026lt;vostra directory di installazione di PIN\u0026gt; $PIN_ROOT/pin -t ./obj-intel64/FuncTracer.so -- example/cov_sample 7 3 Questo comando esegue il nostro binario di destinazione sotto il controllo di Pin, utilizzando la nostra nuova sonda personalizzata. Genera un file di log (pintool.log) con una traccia dettagliata. Diamo un\u0026rsquo;occhiata a un frammento dell\u0026rsquo;output:\nPin: pin-3.31-98869-fa6f126a8 Copyright 2002-2024 Intel Corporation. [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:_init] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:.plt] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:printf@plt] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:atoi@plt] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:fwrite@plt] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:_start] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:_dl_relocate_static_pie] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:deregister_tm_clones] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:register_tm_clones] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:__do_global_dtors_aux] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:frame_dummy] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:main] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:add] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:mul] [tid:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Function:_fini] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_call_libc_early_init.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:remove_slotinfo.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_close_worker.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_map_object_deps.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_fini.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:call_init.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_notify_new_object.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:add_name_to_object.isra.0.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:expand_dynamic_string_token.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_init_paths.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_map_object_from_fd.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_map_object.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_lookup_symbol_x.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:__minimal_realloc.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:_dl_new_object.cold] [tid:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Function:add_to_global_update.cold] ... [tid:10367] [PID:10367] [Image:/lib64/libc.so.6] [Called:pthread_mutex_unlock] [tid:10367] [PID:10367] [Image:/lib64/libc.so.6] [Called:__GI___pthread_mutex_unlock_usercnt] [tid:10367] [PID:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Called:_dl_call_fini] [tid:10367] [PID:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Called:__do_global_dtors_aux] [tid:10367] [PID:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Called:deregister_tm_clones] [tid:10367] [PID:10367] [Image:/home/andrea/CodeCoverage/example/cov_sample] [Called:_fini] [tid:10367] [PID:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Called:_dl_audit_objclose] [tid:10367] [PID:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Called:_dl_call_fini] [tid:10367] [PID:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Called:_dl_audit_objclose] [tid:10367] [PID:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Called:_dl_call_fini] [tid:10367] [PID:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Called:_dl_audit_objclose] [tid:10367] [PID:10367] [Image:/lib64/ld-linux-x86-64.so.2] [Called:_dl_audit_activity_nsid] Poiché il log contiene sia l\u0026rsquo;elenco di tutte le funzioni del nostro binario sia le funzioni che sono state eseguite, è facile preparare uno script che emetta un report di copertura dall\u0026rsquo;aspetto accattivante.\n================================================== Image: /home/andrea/CodeCoverage/example/cov_sample ================================================== Functions Found: 15 Functions Called: 12 Coverage: 80.00% -------------------------------------------------- Called Functions: - .plt - __do_global_dtors_aux - _fini - _init - _start - atoi@plt - deregister_tm_clones - frame_dummy - main - add - printf@plt - register_tm_clones Uncalled Functions: - _dl_relocate_static_pie - mul - fwrite@plt e con un po\u0026rsquo; di formattazione:\nora abbiamo anche un\u0026rsquo;indicazione su dove sia meglio concentrare i nostri test, poiché alcune funzioni del programma non sono state chiamate.\nCome miglioramento, potremmo preparare una \u0026ldquo;whitelist\u0026rdquo; di funzioni che sono intrinseche all\u0026rsquo;ambiente di esecuzione (come main, _start e così via) che possono essere escluse dal report.\n🪩 Andando oltre Sul repository potete trovare alcuni contenuti bonus:\nun programma Python che analizza il log e produce i report di copertura una comoda utilità wrap.sh che prende un binario, lo sostituisce con la chiamata di strumentazione appropriata e opzionalmente ne ripristina lo stato precedente. Prossimo passo: invece di un target fittizio, \u0026ldquo;misureremo\u0026rdquo; i binari del sistema operativo, con automazione completa e senza necessità di ricompilazione. Never give (U) up 🙂‍↔️\np.s. Se non ci avete fatto caso: come easter egg estivo, ogni sezione di questo post è una canzone Eurodance degli anni \u0026lsquo;90 🎧 Buon ascolto!\n","permalink":"https://ilmanzo.github.io/it/post/pintool-function-tracing/","summary":"\u003ch2 id=\"-intro-let-me-be\"\u003e▶️ Intro: \u003ca href=\"https://www.youtube.com/watch?v=mjPVv5ojKTo\"\u003eLet Me Be\u003c/a\u003e\u003c/h2\u003e\n\u003cp\u003eNel \u003ca href=\"https://ilmanzo.github.io/it/post/measuring-test-coverage-on-binaries/\"\u003epost precedente\u003c/a\u003e abbiamo continuato il nostro viaggio affrontando uno scenario più complesso, utilizzando un mix di \u003ccode\u003egdb\u003c/code\u003e e \u003ccode\u003evalgrind\u003c/code\u003e per tracciare l\u0026rsquo;esecuzione di tutte le funzioni all\u0026rsquo;interno di un dato binario.\u003c/p\u003e\n\u003cp\u003eQuesta volta reggetevi forte perché aumenteremo notevolmente la complessità. Ci immergeremo nell\u0026rsquo;analisi di basso livello ed esploreremo come utilizzare \u003ca href=\"https://www.intel.com/content/www/us/en/developer/articles/tool/pin-a-dynamic-binary-instrumentation-tool.html\"\u003eIntel PIN\u003c/a\u003e, un potente framework di strumentazione dinamica per manipolare e ispezionare il codice eseguibile a runtime.\u003c/p\u003e","title":"Quanto codice stai testando? (3)"},{"content":"💡 Il problema Benvenuti di nuovo! È passato un bel po\u0026rsquo; di tempo dalle mie ultime divagazioni sul linguaggio di programmazione D (Dlang)!\nQuesto post nasce da una necessità. Un mio vecchio progetto, i D Koans, utilizzava una libreria esterna per semplificare gli unit test, che costituiscono più o meno il cuore dell\u0026rsquo;intero progetto. Sfortunatamente, la libreria ha iniziato a mostrare alcuni avvisi di deprecazione (deprecation warnings) quando compilata con le versioni recenti di D.\nPoiché il linguaggio D possiede già un framework interno di unit testing, ho pensato che sarebbe stato bello rimuovere l\u0026rsquo;unica dipendenza e fare affidamento solo sulla libreria standard. Inizialmente, con una ricerca e sostituzione globale, sono riuscito a convertire tutti i test in blocchi unittest.\n😫 I traceback dello stack sono brutti L\u0026rsquo;esecuzione di questi test ha presentato un\u0026rsquo;altra sfida. L\u0026rsquo;utilizzo diretto degli unit test standard avrebbe messo gli utenti di fronte a messaggi di errore fitti e complessi, come questo:\ncore.exception.AssertError@koans/alias_this.d(39): unittest failure ---------------- ??:? _d_unittestp [0x4bb84d] koans/alias_this.d:39 void koans.alias_this.__unittest_L34_C1() [0x48b731] ??:? void koans.alias_this.__modtest() [0x48b788] ??:? int core.runtime.runModuleUnitTests().__foreachbody_L603_C5(object.ModuleInfo*) [0x4ccdb2] ??:? int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)).__lambda_L2467_C13(immutable(object.ModuleInfo*)) [0x4b2867] ??:? int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))).__foreachbody_L582_C5(ref rt.sections_elf_shared.DSO) [0x4c1dc7] ??:? int rt.sections_elf_shared.DSO.opApply(scope int delegate(ref rt.sections_elf_shared.DSO)) [0x4c2149] ??:? int rt.minfo.moduleinfos_apply(scope int delegate(immutable(object.ModuleInfo*))) [0x4c1d55] ??:? int object.ModuleInfo.opApply(scope int delegate(object.ModuleInfo*)) [0x4b2839] ??:? runModuleUnitTests [0x4ccbe7] ??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).runAll() [0x4c00dc] ??:? void rt.dmain2._d_run_main2(char[][], ulong, extern (C) int function(char[][])*).tryExec(scope void delegate()) [0x4c0069] ??:? _d_run_main2 [0x4bffd2] ??:? _d_run_main [0x4bfdbb] /usr/include/dlang/dmd/core/internal/entrypoint.d:29 main [0x484a69] ??:? [0x7f94bda2b12d] ??:? __libc_start_main [0x7f94bda2b1f8] \u0026lt;unknown dir\u0026gt;/\u0026lt;unknown file\u0026gt;:115 _start [0x484884] core.exception.AssertError@koans/arrays.d(8): unittest failure il che è tutt\u0026rsquo;altro che ideale per un principiante; inoltre, tutti gli unit test vengono eseguiti in parallelo, quindi si otterrebbe un muro di testo incomprensibile.\n🦸 La metaprogrammazione in aiuto La soluzione è raccogliere tutti gli unit test ed eseguirli manualmente in un ciclo foreach! Questo porta a un altro problema: il progetto è composto di molti moduli, simili a \u0026quot;esercizi\u0026quot; progressivi che lo studente deve completare per imparare. Come facciamo a enumerare tutti i moduli, in un ordine stabilito, e fare in modo che il programma principale li importi e possa chiamare le loro funzioni? Permettetemi di presentarvi la metaprogrammazione :)\nDato che tutti gli esercizi si trovano in una directory, è facile raggrupparli in un singolo modulo package (package module):\n$ tree . ├── dscanner.ini ├── dub.json ├── koans │ ├── alias_this.d │ ├── arrays.d │ ├── associative_arrays.d │ ├── basics.d │ ├── bitwise_operators.d │ ├── chars.d │ ├── c_interop.d │ ├── classes.d │ ├── concurrency.d │ ├── ctfe.d │ ├── delegates.d │ ├── enums.d │ ├── exceptions.d │ ├── files.d │ ├── foreach_loop.d │ ├── function_parameters.d │ ├── helpers.d │ ├── lambda_syntax.d │ ├── mixins.d │ ├── numbers.d │ ├── operator_overloading.d │ ├── package.d \u0026lt;--------------- QUESTO │ ├── pointers.d │ ├── properties.d │ ├── strings.d │ ├── structs.d │ ├── templates.d │ ├── traits.d │ ├── tuples.d │ └── unions.d ├── learn.d ├── README.md └── scripts ├── runner_linux.sh └── runner_osx.sh Il nostro package.d è semplice:\n1 2 3 4 5 6 7 8 9 10 // koans/package.d module koans; static immutable koansModules = [ \u0026#34;basics\u0026#34;, \u0026#34;numbers\u0026#34;, \u0026#34;chars\u0026#34;, \u0026#34;strings\u0026#34;, // ... enumera tutti i moduli degli esercizi ]; static foreach (m; koansModules) mixin(\u0026#34;public static import koans.\u0026#34; ~ m ~ \u0026#34;;\u0026#34;); invece di importare tutti i moduli singolarmente, usiamo un ciclo per creare a tempo di compilazione (compile time) le istruzioni di importazione. In questo modo, il programma principale deve solo importare import koans come pacchetto completo.\nnota: riutilizzeremo lo stesso elenco di moduli nel programma principale (main):\n⚙️ Un test runner personalizzato 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 // learn.d module learn; import core.runtime; import std.stdio; import koans; static import core.exception; shared static this() { // Sovrascrive il test runner predefinito per non fare nulla. // Dopodiché, verrà chiamato il \u0026#34;main\u0026#34;. Runtime.moduleUnitTester = { return true; }; } void main() { writeln(\u0026#34;Starting your journey to enlightenment...\u0026#34;); writeln(\u0026#34;You will be asked to fill in the blanks in the koans.\u0026#34;); writeln(\u0026#34;Ensure to run \u0026#39;dub --build=unittest\u0026#39; to run the tests.\u0026#34;); static foreach (m; koans.koansModules) { mixin(\u0026#34;static import koans.\u0026#34; ~ m ~ \u0026#34;;\u0026#34;); foreach (t; __traits(getUnitTests, mixin(\u0026#34;koans.\u0026#34; ~ m))) { try t(); catch (core.exception.AssertError e) { writeln(\u0026#34;Meditate more on \u0026#34;, e.file, \u0026#34; at line \u0026#34;, e.line); return; } } } writeln(\u0026#34;You have reached the end of your journey\u0026#34;); } Le parti importanti sono:\nrighe 9-14: è necessario sovrascrivere la funzione predefinita Runtime.moduleUnitTester. Questo permetterà al nostro main di essere eseguito anche quando il programma viene compilato con il flag --unittest. riga 21: iteriamo su ogni modulo, riutilizzando lo stesso array di stringhe definito in precedenza in package.d. riga 23: costruiamo un\u0026rsquo;istruzione di importazione con ambito (scoped import) con il nome del modulo, preceduto dal nome del pacchetto (es. koans.basics). riga 24: usiamo i traits per scorrere tutti gli unit test di quel modulo, eseguendo l\u0026rsquo;unit test (che è incapsulato come una funzione) all\u0026rsquo;interno di un blocco try-catch per catturare l\u0026rsquo;errore AssertError. riga 29: se l\u0026rsquo;unit test fallisce, forniamo all\u0026rsquo;utente istruzioni su quale riga di quale file deve essere modificata e il programma termina. (immancabile immagine accattivante generata dall\u0026rsquo;IA)\n✅ Conclusioni Il mio progetto ora non dipende da nessun\u0026rsquo;altra libreria e sarà estremamente semplice aggiungere nuovi test: basta seguire le convenzioni del linguaggio e creare un nuovo file con gli unit test, per poi scriverne il nome nella posizione corretta dell\u0026rsquo;array.\nSpero che questo esempio pratico delle capacità di D sia stato interessante. E, cosa più importante, vi ha incuriosito abbastanza da voler approfondire la conoscenza del linguaggio D?\nAvete mai usato la metaprogrammazione di D per compiti simili? I feedback sono i benvenuti!\n","permalink":"https://ilmanzo.github.io/it/post/refactoring-d-koans-with-metaprogramming/","summary":"\u003ch2 id=\"-il-problema\"\u003e💡 Il problema\u003c/h2\u003e\n\u003cp\u003eBenvenuti di nuovo! È passato un bel po\u0026rsquo; di tempo dalle mie \u003ca href=\"https://ilmanzo.github.io/post/fileinput-for-d-programming-language/\"\u003eultime divagazioni\u003c/a\u003e sul \u003ca href=\"https://dlang.org/\"\u003elinguaggio di programmazione D (Dlang)\u003c/a\u003e!\u003c/p\u003e\n\u003cp\u003eQuesto post nasce da una necessità. Un mio vecchio progetto, i \u003ca href=\"https://github.com/ilmanzo/DLangKoans\"\u003eD Koans\u003c/a\u003e, utilizzava una libreria esterna per semplificare gli unit test, che costituiscono più o meno il cuore dell\u0026rsquo;intero progetto. Sfortunatamente, la libreria ha iniziato a mostrare \u003ca href=\"https://github.com/linkrope/dunit/issues/36\"\u003ealcuni avvisi di deprecazione (deprecation warnings)\u003c/a\u003e quando compilata con le versioni recenti di D.\u003c/p\u003e","title":"Refactoring dei D Koans con la metaprogrammazione"},{"content":"▶️ Introduzione Nel post precedente abbiamo iniziato il nostro viaggio con uno scenario molto semplice, e abbiamo utilizzato una comoda funzionalità del linguaggio di programmazione Go per misurare quale percentuale del programma target viene esercitata dal nostro test.\nQuesta volta sperimenterò una Proof of Concept su come ottenere una stima della metrica di copertura del codice di test per un normale programma binario, senza alcuna ricompilazione.\nIn questo esempio faremo finta che il nostro compito sia scrivere test di integrazione per il famoso programma gzip, e cercheremo di misurare i progressi che stiamo facendo riguardo alla copertura dei nostri test.\nAnche gli animali domestici hanno bisogno di copertura! Crediti immagine: Em Hopper\n🧮 Come? L\u0026rsquo;idea principale è:\nottenere in qualche modo l\u0026rsquo;elenco completo delle funzioni presenti nel programma = N registrare, durante il test, quali di queste funzioni vengono eseguite = E Il rapporto E/N fornisce un\u0026rsquo;approssimazione dell\u0026rsquo;efficacia del test, guidandoci verso le aree che necessitano di un\u0026rsquo;estensione della copertura.\nNon vogliamo ricompilare gzip con la strumentazione di copertura, ma nella nostra distribuzione abbiamo le informazioni di debug (debuginfo) del programma. Di solito sono fornite in pacchetti separati e il repository non è abilitato di default, quindi prima di tutto abilitiamoli e installiamo i relativi pacchetti. Su Tumbleweed:\n$ sudo zypper modifyrepo -e repo-debug $ sudo zypper refresh $ sudo zypper in gzip-debuginfo gzip-debugsource 👐 Funzioni fino in fondo Possiamo usare il debugger gdb per avere un elenco di tutte le funzioni in un programma:\n$ sudo zypper install gdb $ gdb /usr/bin/gzip For help, type \u0026#34;help\u0026#34;. Type \u0026#34;apropos word\u0026#34; to search for commands related to \u0026#34;word\u0026#34;... Reading symbols from /usr/bin/gzip... Reading symbols from /usr/lib/debug/usr/bin/gzip.debug... (gdb) info functions All defined functions: File ../sysdeps/x86_64/start.S: void _start(void); File ./lib/stat-time.h: 29: int openat_safer(int, const char *, int, ...); 30: int rpl_printf(const char *, ...); 116: int unzip(int, int); [... long output omitted ...] Sembra promettente!\n☝️ Scrivere il primo test Come l\u0026rsquo;ultima volta, per semplicità useremo il framework pytest, ma qualsiasi altro andrebbe altrettanto bene. Per prima cosa, scriviamo uno smoke test:\n# test_gzip.py import os,re from subprocess import run PROGRAM=\u0026#39;/usr/bin/gzip\u0026#39; # program should display help def test_help(capfd): process=run([PROGRAM,\u0026#39;-h\u0026#39;]) stdout, stderr = capfd.readouterr() assert process.returncode == 0 assert \u0026#34;Usage:\u0026#34; in stdout In questo test, avviamo un processo per eseguire semplicemente gzip -h, aspettandoci un output specifico. eseguiamolo:\n============================= test session starts ============================== platform linux -- Python 3.13.2, pytest-8.3.4, pluggy-1.5.0 rootdir: /home/andrea/binarycoverage collected 1 item test_gzip.py . [100%] ============================== 1 passed in 0.01s =============================== 👣 Tracciarlo Ora possiamo tracciare quali funzioni sono state esercitate avvolgendo l\u0026rsquo;esecuzione del test con il potente strumento valgrind:\n$ sudo zypper install valgrind $ valgrind --tool=callgrind --trace-children=yes pytest l\u0026rsquo;esecuzione richiede un po\u0026rsquo; più di tempo e otteniamo alcuni nuovi file che contengono i dati di tracciamento:\n$ ls -l callgrind.out.* -rw-------. 1 andrea andrea 1944681 Mar 30 17:54 callgrind.out.2771 -rw-------. 1 andrea andrea 82977 Mar 30 17:54 callgrind.out.2816 Questi file di dati sono destinati a essere elaborati da callgrind_annotate che produrrà un report dettagliato con tutte le funzioni eseguite (comprese quelle in librerie come glibc).\n$ callgrind_annotate callgrind.out.2816 -------------------------------------------------------------------------------- Profile data file \u0026#39;callgrind.out.2816\u0026#39; (creator: callgrind-3.24.0) -------------------------------------------------------------------------------- I1 cache: D1 cache: LL cache: Timerange: Basic block 0 - 52685 Trigger: Program termination Profiled target: /usr/bin/gzip -h (PID 2816, part 1) Events recorded: Ir Events shown: Ir Event sort order: Ir Thresholds: 99 Include dirs: User annotated: Auto-annotation: on -------------------------------------------------------------------------------- Ir -------------------------------------------------------------------------------- 246,004 (100.0%) PROGRAM TOTALS -------------------------------------------------------------------------------- Ir file:function -------------------------------------------------------------------------------- 41,382 (16.82%) /usr/src/debug/glibc-2.41/elf/dl-lookup.c:do_lookup_x [/usr/lib64/ld-linux-x86-64.so.2] 40,596 (16.50%) /usr/src/debug/glibc-2.41/elf/dl-reloc.c:_dl_relocate_object_no_relro [/usr/lib64/ld-linux-x86-64.so.2] 17,388 ( 7.07%) /usr/src/debug/glibc-2.41/elf/dl-lookup.c:_dl_lookup_symbol_x [/usr/lib64/ld-linux-x86-64.so.2] 13,781 ( 5.60%) /usr/src/debug/glibc-2.41/elf/dl-tunables.c:__GI___tunables_init [/usr/lib64/ld-linux-x86-64.so.2] 13,309 ( 5.41%) /usr/src/debug/glibc-2.41/elf/../sysdeps/generic/dl-new-hash.h:_dl_lookup_symbol_x 11,941 ( 4.85%) /usr/src/debug/glibc-2.41/string/../sysdeps/x86_64/multiarch/../multiarch/strcmp-sse2.S:strcmp [/usr/lib64/ld-linux-x86-64.so.2] 9,951 ( 4.05%) /usr/src/debug/glibc-2.41/elf/dl-lookup.c:check_match [/usr/lib64/ld-linux-x86-64.so.2] 8,321 ( 3.38%) /usr/src/debug/glibc-2.41/elf/do-rel.h:_dl_relocate_object_no_relro 7,033 ( 2.86%) /usr/src/debug/gzip-1.13/lib/vasnprintf.c:vasnprintf [/usr/bin/gzip] 6,968 ( 2.83%) /usr/src/debug/glibc-2.41/elf/../sysdeps/x86_64/dl-machine.h:_dl_relocate_object_no_relro 5,935 ( 2.41%) /usr/src/debug/glibc-2.41/elf/../sysdeps/x86/dl-cacheinfo.h:intel_check_word.constprop.0 [/usr/lib64/ld-linux-x86-64.so.2] 4,811 ( 1.96%) /usr/src/debug/glibc-2.41/elf/../bits/stdlib-bsearch.h:intel_check_word.constprop.0 4,402 ( 1.79%) /usr/src/debug/glibc-2.41/elf/dl-version.c:_dl_check_map_versions [/usr/lib64/ld-linux-x86-64.so.2] 4,356 ( 1.77%) /usr/src/debug/glibc-2.41/elf/dl-tunables.h:__GI___tunables_init 4,348 ( 1.77%) /usr/src/debug/gzip-1.13/lib/printf-parse.c:vasnprintf 2,660 ( 1.08%) /usr/src/debug/glibc-2.41/stdio-common/vfprintf-internal.c:__printf_buffer [/usr/lib64/libc.so.6] 2,064 ( 0.84%) /usr/src/debug/glibc-2.41/stdio-common/Xprintf_buffer_write.c:__printf_buffer_write [/usr/lib64/libc.so.6] Sebbene sia un po\u0026rsquo; prolisso, contiene tutte le informazioni di cui abbiamo bisogno. Ha solo bisogno di una sistematina\u0026hellip;\n🤖 Automatizzarlo Per renderci la vita più facile, conviene usare un semplice script d\u0026rsquo;appoggio per automatizzare gli strumenti e analizzare i dati tramite codice Python, così da ottenere le informazioni necessarie. Il progetto completo è disponibile sul mio repository GitHub, ma ecco un estratto dello script coverage.sh che esegue pytest e produce la misura di copertura:\n#!/bin/bash BINARY=gzip TEMP_DIR=$(mktemp -d) valgrind --tool=callgrind --trace-children=yes \\ --callgrind-out-file=$TEMP_DIR/callgrind.%p pytest 2\u0026gt; /dev/null # dump all the functions in the binary gdb -ex \u0026#39;set pagination off\u0026#39; -ex \u0026#39;info functions\u0026#39; -ex quit \\ $(which $BINARY) \u0026gt; $TEMP_DIR/all_funcs.gdb python3 calc_coverage.py --binary $BINARY -d $TEMP_DIR # Clean up: Remove the temporary directory and its contents rm -rf \u0026#34;$TEMP_DIR\u0026#34; \u0026gt; ./coverage.sh ============================= test session starts ============================== platform linux -- Python 3.13.2, pytest-8.3.4, pluggy-1.5.0 rootdir: /home/andrea/binarycoverage collected 1 item test_gzip.py . [100%] ============================== 1 passed in 0.54s =============================== --- Binary coverage report --- Functions coverage: 9/80 11.25% Come previsto, il nostro \u0026ldquo;smoke\u0026rdquo; test su gzip esegue solo 9 funzioni su 80, con una bassa copertura dell'11%.\n🏃‍➡️ Andiamo avanti Ora possiamo migliorare i nostri test, guidati dalla metrica di copertura. Proviamo con l\u0026rsquo;opzione gzip -V?\n# program should display version information def test_version(capfd): process=run([PROGRAM,\u0026#39;-V\u0026#39;]) stdout, stderr = capfd.readouterr() assert process.returncode == 0 assert \u0026#34;This is free software\u0026#34; in stdout assert re.search(r\u0026#34;gzip \\d.\\d\\d\u0026#34;, stdout) Un semplice test per assicurarsi che il programma restituisca una versione numerica.\n$ ./coverage.sh ============================= test session starts ============================== collected 2 items test_gzip.py .. [100%] ============================== 2 passed in 1.17s =============================== --- Binary coverage report --- Functions coverage: 10/80 12.50% Un po\u0026rsquo; meglio! Aggiungiamo un test negativo per sicurezza:\n# program should fail when given a non existing file def test_compress_non_existent(): process=run([PROGRAM,\u0026#39;foobar\u0026#39;]) assert process.returncode==1 $ ./coverage.sh ============================= test session starts ============================== collected 3 items test_gzip.py ... [100%] ============================== 3 passed in 1.51s =============================== --- Binary coverage report --- Functions coverage: 19/80 23.75% Siamo sulla buona strada. Abbiamo raddoppiato la copertura, e ancora non abbiamo compresso nulla\u0026hellip;\n🏋️ Facciamo del lavoro vero È ora di scrivere un test per comprimere e decomprimere un file! Introduciamo anche una funzione di supporto (helper) nel test, poiché ne avremo bisogno più di una volta:\nSAMPLE_FILE=\u0026#39;sample.txt\u0026#39; # program should compress and de-compress a file def test_compress_decompress(capfd): create_test_file(SAMPLE_FILE) with open(SAMPLE_FILE) as file: content=file.readlines() process=run([PROGRAM,SAMPLE_FILE]) assert process.returncode == 0 compressed_file=SAMPLE_FILE+\u0026#34;.gz\u0026#34; # decompress and read back content process=run([PROGRAM,\u0026#39;-d\u0026#39;,compressed_file]) assert process.returncode == 0 with open(SAMPLE_FILE) as file: assert(file.readlines()==content) os.remove(SAMPLE_FILE) # helper function to create a dummy sample file def create_test_file(file_name): sample_text = \u0026#34;\u0026#34;\u0026#34;This is a dummy sample text file. It contains some random lines of text. This is line 3 of the text file. Here is line 4, just for testing purposes. Feel free to modify or extend this text. \u0026#34;\u0026#34;\u0026#34; # Open the file in write mode (\u0026#39;w\u0026#39;) and write the sample text to it with open(file_name, \u0026#39;w\u0026#39;) as file: file.write(sample_text) \u0026gt; ./coverage.sh ============================= test session starts ============================== collected 4 items test_gzip.py .... [100%] ============================== 4 passed in 2.30s =============================== --- Binary coverage report --- Functions coverage: 52/80 65.00% Questo è un grande progresso! I nostri test stanno migliorando. Ne facciamo un altro? Passiamo al lato oscuro e diamogli un file danneggiato:\n# program should give error on a damaged compressed file def test_decompress_error(capfd): wrong_file=\u0026#39;dummy.txt\u0026#39; create_test_file(wrong_file) wrong_compressed=wrong_file+\u0026#39;.gz\u0026#39; process=run([PROGRAM,wrong_file]) # now damage the compressed file by writing a random byte with open(wrong_compressed, \u0026#34;r+b\u0026#34;) as file: file.seek(32) file.write(bytes(0xFF)) # decompression should fail process=run([PROGRAM,\u0026#39;-d\u0026#39;,wrong_compressed]) stdout, stderr = capfd.readouterr() assert process.returncode==1 assert \u0026#39;invalid compressed data\u0026#39; in stderr os.remove(wrong_file+\u0026#39;.gz\u0026#39;) $ ./coverage.sh ============================= test session starts ============================== collected 5 items test_gzip.py ..... [100%] ============================== 5 passed in 3.02s =============================== --- Binary coverage report --- Functions coverage: 54/80 67.50% Questo è un ottimo risultato! Vi vengono in mente aree di miglioramento?\n💡 Ci sfugge qualcosa Se usate l\u0026rsquo;opzione verbosa -v, lo script Python calc_coverage mostrerà le funzioni che sono state testate e quelle che non lo sono state:\nExecuted functions: atdir_eq,atdir_set,bi_windup,build_tree,compress_block,ct_tally,discard_input_bytes,do_exit,fd_safer,file_read,fill_inbuf,fill_window,finish_out,finish_up_gzip,flush_block,flush_outbuf,flush_window,gen_codes,get_input_size_and_time,get_method,get_suffix,huft_build,huft_free,inflate_codes,inflate_dynamic,init_block,input_eof,last_component,license,longest_match,main,open_and_stat,open_safer,openat_safer,pqdownheap,progerror,read_buffer,remove_output_file,rpl_fclose,rpl_fflush,rpl_fprintf,rpl_printf,rpl_vfprintf,scan_tree,send_bits,send_tree,strlwr,treat_file,unzip,updcrc,vasnprintf,write_buf,xstrdup,zip Missing functions : _start,abort_gzip_signal,copy,copy_block,direntry_cmp_name,display_ratio,do_list,fillbuf,fprint_off,gzip_error,inflate_fixed,make_table,mbszero,read_byte,read_error,read_pt_len,rpl_fcntl,rsync_roll,treat_stdin,try_help,unlzh,unlzw,unpack,write_error,xalloc_die,xpalloc In questo modo, abbiamo anche alcuni indizi su quali funzionalità del programma non stiamo testando. In questo esempio, tra le altre cose possiamo citare la compatibilità con rsync e il supporto per i file .Z. Naturalmente, alcune (como le routine di gestione dei segnali) sono molto difficili da testare adeguatamente.\n🧵 Considerazioni finali È fondamentale ricordare che la percentuale di copertura ottenuta con questo metodo è un\u0026rsquo;approssimazione. valgrind traccia le chiamate alle funzioni, non le singole righe o i rami di esecuzione. Pertanto, una funzione potrebbe essere chiamata ma non completamente testata, portando a potenziali falsi positivi. Inoltre, le funzioni esercitate indirettamente da altre chiamate potrebbero non essere esplicitamente elencate, con conseguenti falsi negativi. Il sovraccarico di prestazioni introdotto da valgrind significa anche che questa tecnica è più adatta per analisi offline che per test in tempo reale.\nD\u0026rsquo;altra parte, offre il vantaggio di essere semplice da implementare, non richiede grandi sforzi né configurazioni particolari e potete usarlo come indicatore per capire se i test di integrazione che state scrivendo stanno migliorando nel tempo o meno. Un altro buon utilizzo può essere quello di rilevare quando una nuova versione dei programmi introduce più funzionalità, poiché se la copertura diminuisce con l\u0026rsquo;aggiornamento, significa che non state testando le novità.\nGrazie per avermi seguito fino alla fine di questo lungo post, sentitevi liberi di inviare commenti e feedback, happy hacking! \u0026#x1f44b;\n","permalink":"https://ilmanzo.github.io/it/post/measuring-test-coverage-on-binaries/","summary":"\u003ch2 id=\"-introduzione\"\u003e▶️ Introduzione\u003c/h2\u003e\n\u003cp\u003eNel \u003ca href=\"https://ilmanzo.github.io/it/post/measuring-coverage-of-integration-tests/\"\u003epost precedente\u003c/a\u003e abbiamo iniziato il nostro viaggio con uno scenario molto semplice, e abbiamo utilizzato una \u003ca href=\"https://go.dev/blog/integration-test-coverage\"\u003ecomoda funzionalità\u003c/a\u003e del linguaggio di programmazione Go per misurare quale percentuale del programma target viene esercitata dal nostro test.\u003c/p\u003e\n\u003cp\u003eQuesta volta sperimenterò una Proof of Concept su come ottenere una stima della metrica di copertura del codice di test per un normale programma binario, \u003cstrong\u003esenza alcuna ricompilazione.\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003eIn questo esempio faremo finta che il nostro compito sia scrivere test di integrazione per il famoso programma \u003ccode\u003egzip\u003c/code\u003e, e cercheremo di misurare i progressi che stiamo facendo riguardo alla \u003cem\u003ecopertura\u003c/em\u003e dei nostri test.\u003c/p\u003e","title":"Quanto codice stai testando? (2)"},{"content":"☂️ Introduzione Quando il vostro codice include una suite di unit test, la copertura del codice (code coverage) è una metrica importante per misurare l\u0026rsquo;efficacia dei test ed è piuttosto facile da ottenere; ci sono moltissimi strumenti in circolazione.\nCrediti immagine: Nataliya Vaitkevich\nD\u0026rsquo;altra parte, spesso abbiamo anche bisogno di eseguire test di integrazione o E2E (end-to-end), poiché nei nostri flussi di QA eseguiamo principalmente applicazioni reali piuttosto che singole funzioni isolate.\nIniziamo con un caso d\u0026rsquo;uso di base e prepariamo un semplice programma su misura per questo scopo.\nSupponiamo di voler testare un semplice programma che stampa \u0026quot;Hello, World!\u0026quot; con alcuni argomenti opzionali sulla riga di comando (potete trovare il codice sorgente in fondo 👇):\n$ ./hello --help Usage of ./hello: -count int number of times to repeat (default 1) -header print also a fancy header -name string your name for greeting (default \u0026#34;World\u0026#34;) -upper convert to uppercase 🚦 Rosso, Verde, Refactoring Useremo il framework pytest, ma qualsiasi altro andrebbe altrettanto bene. Come primo passo, scriviamo un test fallimentare (rosso):\n# test_hello.py from subprocess import run def smoke(capfd): run([\u0026#34;./hello\u0026#34;]) out, err = capfd.readouterr() assert out == \u0026#34;\u0026#34; esecuzione semplice:\n$ pytest ============================ test session starts ============================= platform linux -- Python 3.11.11, pytest-8.3.4, pluggy-1.5.0 rootdir: /home/andrea/projects/test collected 1 item test_hello.py F [100%] ================================== FAILURES ================================== _________________________________ test_hello _________________________________ capfd = \u0026lt;_pytest.capture.CaptureFixture object at 0x7f3824e41650\u0026gt; def test_smoke(capfd): run([\u0026#34;./hello\u0026#34;]) out, err = capfd.readouterr() \u0026gt; assert out == \u0026#34;\u0026#34; E AssertionError: assert \u0026#39;Hello, World!\\n\u0026#39; == \u0026#39;\u0026#39; E E + Hello, World! test_hello.py:6: AssertionError ========================== short test summary info =========================== FAILED test_hello.py::test_smoke - AssertionError: assert \u0026#39;Hello, World!\\n\u0026#39; == \u0026#39;\u0026#39; ============================= 1 failed in 0.02s ============================== Ebbene sì\u0026hellip; Dobbiamo verificare l\u0026rsquo;output corretto. Niente di più semplice:\n# test_hello.py from subprocess import run def test_smoke(capfd): run([\u0026#34;./hello\u0026#34;]) out, err = capfd.readouterr() assert out == \u0026#34;Hello, World!\\n\u0026#34; $ pytest ============================ test session starts ============================= platform linux -- Python 3.11.11, pytest-8.3.4, pluggy-1.5.0 rootdir: /home/andrea/projects/test collected 1 item test_hello.py . [100%] ============================= 1 passed in 0.01s ============================== 📣 Dillo più forte Siamo degli ingegneri QA fantastici e in gamba, vero? 😎 Quindi, osservando l\u0026rsquo;help della riga di comando del programma, decidiamo di scrivere un secondo test per provare la funzionalità del testo in maiuscolo (uppercase):\n# test_hello.py from subprocess import run def test_smoke(capfd): run([\u0026#34;./hello\u0026#34;]) out, err = capfd.readouterr() assert out == \u0026#34;Hello, World!\\n\u0026#34; def test_uppercase(capfd): run([\u0026#34;./hello\u0026#34;, \u0026#34;-upper\u0026#34;]) out, err = capfd.readouterr() assert out == \u0026#34;HELLO, WORLD!\\n\u0026#34; $ pytest ============================== test session starts =============================== platform linux -- Python 3.11.11, pytest-8.3.4, pluggy-1.5.0 rootdir: /home/andrea/projects/test collected 2 items test_hello.py .. [100%] =============================== 2 passed in 0.02s ================================ 🎉 🎉 Abbiamo raddoppiato la copertura dei test, festeggiamo! 🎉 🎉 Il nostro lavoro sembra finito\u0026hellip;\nAspettate\u0026hellip; In realtà, come si fa a dire quando i nostri test sono abbastanza buoni? Quanto del codice del programma state effettivamente eseguendo? I nostri test evitano o trascurano qualche funzionalità chiave?\n🌡️ Introduciamo lo strumento di misura Per ogni esecuzione del test, dobbiamo misurare il rapporto tra il codice eseguito e il codice totale del programma. Questo è un argomento complesso e dipende principalmente da come è costruito il programma, ma come primo passo possiamo utilizzare una funzionalità che Go ha introdotto un paio di anni fa, a partire dalla versione 1.20.\nQuindi, ricompiliamo il programma con le informazioni di copertura nel binario:\n$ go build -cover hello.go Una volta compilato, il programma ci dà un indizio sul fatto che qualcosa è cambiato:\n$ ./hello warning: GOCOVERDIR not set, no coverage data emitted Hello, World! Il compilatore Go ha strumentato il programma, e ora dobbiamo fornirgli un percorso in cui memorizzare i dati di copertura raccolti. Quindi, prepariamo un semplice script che configurerà l\u0026rsquo;ambiente ed eseguirà anche la nostra suite di test:\n$ cat cov_test.sh #!/bin/sh rm -rf covdatafiles mkdir covdatafiles rm hello \u0026amp;\u0026amp; go build -cover hello.go GOCOVERDIR=covdatafiles pytest L\u0026rsquo;esecuzione apparentemente non è cambiata:\n$ ./cov_test.sh ============================== test session starts =============================== platform linux -- Python 3.11.11, pytest-8.3.4, pluggy-1.5.0 rootdir: /home/andrea/projects/test collected 2 items test_hello.py .. [100%] =============================== 2 passed in 0.02s ================================ ma ora abbiamo dei dati in una nuova sottocartella:\n$ ls covdatafiles covcounters.6d07efc23254e1696fe8a1428981e28e.13877.1740324625565161919 covcounters.6d07efc23254e1696fe8a1428981e28e.13882.1740324625569223562 covmeta.6d07efc23254e1696fe8a1428981e28e questi file sono destinati a essere elaborati da un altro strumento:\n$ go tool covdata percent -i covdatafiles command-line-arguments\tcoverage: 85.7% of statements 🐤 Un altro piccolo passo aggiungiamo un altro test e vediamo se la nostra copertura aumenta:\nfrom subprocess import run def test_smoke(capfd): run([\u0026#34;./hello\u0026#34;]) out, err = capfd.readouterr() assert out == \u0026#34;Hello, World!\\n\u0026#34; def test_uppercase(capfd): run([\u0026#34;./hello\u0026#34;, \u0026#34;-upper\u0026#34;]) out, err = capfd.readouterr() assert out == \u0026#34;HELLO, WORLD!\\n\u0026#34; def test_header(capfd): run([\u0026#34;./hello\u0026#34;, \u0026#34;-header\u0026#34;]) out, err = capfd.readouterr() assert out == \u0026#34;-------------\\nHello, World!\\n\u0026#34; 🚀 Evviva! $ go tool covdata percent -i covdatafiles command-line-arguments\tcoverage: 92.9% of statements Abbiamo fatto un passo avanti e possiamo tranquillamente affermare che i nostri test sono migliori di prima.\nGià che ci siamo, integriamo l\u0026rsquo;output della copertura nel nostro script di test:\n$ cat cov_test.sh #!/bin/sh rm -rf covdatafiles mkdir covdatafiles rm hello \u0026amp;\u0026amp; go build -cover hello.go GOCOVERDIR=covdatafiles pytest go tool covdata percent -i covdatafiles 🔎 Usa la forza del sorgente, Luke Ora una domanda per il lettore; guardando il codice sorgente del programma:\npackage main import ( \u0026#34;flag\u0026#34; \u0026#34;fmt\u0026#34; \u0026#34;strings\u0026#34; ) func main() { name := flag.String(\u0026#34;name\u0026#34;, \u0026#34;World\u0026#34;, \u0026#34;your name for greeting\u0026#34;) count := flag.Int(\u0026#34;count\u0026#34;, 1, \u0026#34;number of times to repeat\u0026#34;) isUpper := flag.Bool(\u0026#34;upper\u0026#34;, false, \u0026#34;convert to uppercase\u0026#34;) addHeader := flag.Bool(\u0026#34;header\u0026#34;, false, \u0026#34;print also a fancy header\u0026#34;) flag.Parse() message := fmt.Sprintf(\u0026#34;Hello, %s!\u0026#34;, *name) if *name == \u0026#34;Andrea\u0026#34; { message += \u0026#34; Welcome back!\u0026#34; } if *isUpper { message = strings.ToUpper(message) } if *addHeader { fmt.Println(strings.Repeat(\u0026#34;-\u0026#34;, len(message))) } for i := 0; i \u0026lt; *count; i++ { fmt.Println(message) } } vi viene in mente un ultimo test da scrivere per raggiungere il 100% di copertura? 😉\nOttima notizia: possiamo avere un indizio visivo producendo un output HTML! Dobbiamo solo aggiungere un altro paio di righe di post-elaborazione:\n$ go tool covdata textfmt -i=covdatafiles -o=coverage.txt $ go tool cover -html coverage.txt Ebbene sì, ora è molto chiaro cosa ci stiamo perdendo \u0026#x1f604;\ndef test_andrea(capfd): run([\u0026#34;./hello\u0026#34;,\u0026#34;-name\u0026#34;,\u0026#34;Andrea\u0026#34;]) out, err = capfd.readouterr() assert out == \u0026#34;Hello, Andrea! Welcome back!\\n\u0026#34; 🧪 Considerazioni finali Grazie all\u0026rsquo;eccellente strumentazione fornita da Go, aggiungere informazioni di copertura ai binari compilati è semplicissimo e possiamo finalmente avere un\u0026rsquo;idea di quanto codice i nostri test stanno effettivamente sondando.\nSono sicuro che sia una metrica molto importante da avere, quindi possiamo pensare a dei modi per estendere questo concetto ad altri linguaggi e tecnologie.\naggiornamento: c\u0026rsquo;è un post di follow-up che potete leggere qui\nSentitevi liberi di lasciarmi commenti e feedback, buon hacking! \u0026#x1f44b;\n","permalink":"https://ilmanzo.github.io/it/post/measuring-coverage-of-integration-tests/","summary":"\u003ch2 id=\"-introduzione\"\u003e☂️ Introduzione\u003c/h2\u003e\n\u003cp\u003eQuando il vostro codice include una suite di unit test, la \u003ca href=\"https://en.wikipedia.org/wiki/Code_coverage\"\u003ecopertura del codice (code coverage)\u003c/a\u003e è una metrica importante per misurare l\u0026rsquo;efficacia dei test ed è piuttosto facile da ottenere; ci sono \u003ca href=\"https://www.browserstack.com/guide/code-coverage-tools\"\u003emoltissimi strumenti in circolazione\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"metrics\" loading=\"lazy\" src=\"/img/pexels-n-voitkevich-6120217.jpg\"\u003e\nCrediti immagine: \u003ca href=\"https://www.pexels.com/it-it/@n-voitkevich/\"\u003eNataliya Vaitkevich\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eD\u0026rsquo;altra parte, spesso abbiamo anche bisogno di eseguire test di integrazione o E2E (end-to-end), poiché nei nostri flussi di QA eseguiamo principalmente applicazioni reali piuttosto che singole funzioni isolate.\u003c/p\u003e","title":"Quanto codice stai testando? (1)"},{"content":"💭 Di cosa si tratta? Immaginate un server web che si avvia solo quando qualcuno tenta effettivamente di accedervi. O un database che si attiva solo quando arriva una query: questa è la magia della socket activation (attivazione tramite socket). Il concetto non è nuovo, poiché gli amministratori di sistema di vecchia scuola ricorderanno l\u0026rsquo;uso in passato di strumenti come inetd o xinetd per l\u0026rsquo;attivazione dei servizi su richiesta (on-demand).\nPoiché alcuni progetti interessanti come cockpit hanno già iniziato a utilizzare questa funzionalità poco conosciuta, in questo blog post vedremo le basi e cercheremo di familiarizzare con gli strumenti.\n🔑 Sotto il cofano I componenti chiave sono:\nun file di unità .socket: definisce il socket (porta, protocollo) su cui ascoltare. un file di unità .service: definisce il servizio da avviare al momento della connessione. systemd associa il file .socket al file .service:\nsystemd ascolta sul socket Un client si connette al socket systemd rileva la connessione systemd avvia il servizio associato systemd passa il socket al servizio Il servizio ora gestisce direttamente la connessione 🔨 Mettiamoci alla prova Partiamo da zero: OpenSUSE Leap 16.0 è in fase di α-test, quindi possiamo usarlo come campo di gioco \u0026#x1f604; ma alla fine potete usare la distribuzione che preferite, a patto che sia dotata del gestore dei servizi systemd.\nCome scenario dimostrativo, supponiamo che abbiate creato un fantastico dice-as-a-service™ (servizio di lancio dadi) che restituisce un numero casuale ogni volta che viene invocato. Naturalmente è basato su REST e JSON!\nfrom flask import Flask, jsonify import random app = Flask(__name__) @app.route(\u0026#39;/roll\u0026#39;) def roll_dice(): return jsonify({\u0026#34;result\u0026#34;: random.randint(1, 6)}) (nota: questo è solo un esempio, un\u0026rsquo;applicazione di produzione vera e propria dovrebbe controllare gli input, gestire gli errori, registrare i log in modo appropriato, e così via)\n$ sudo zypper in python3-Flask $ flask --app dice.py run \u0026amp; [1] 10100 andrea@toolbox-andrea-user:/tmp\u0026gt; * Serving Flask app \u0026#39;dice.py\u0026#39; * Debug mode: off WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead. * Running on http://127.0.0.1:5000 Press CTRL+C to quit Testiamolo: $ curl http://127.0.0.1:5000/roll 127.0.0.1 - - [01/Feb/2025 10:30:46] \u0026#34;GET /roll HTTP/1.1\u0026#34; 200 - {\u0026#34;result\u0026#34;:2} $ curl http://127.0.0.1:5000/roll 127.0.0.1 - - [01/Feb/2025 10:30:49] \u0026#34;GET /roll HTTP/1.1\u0026#34; 200 - {\u0026#34;result\u0026#34;:1} $ curl http://127.0.0.1:5000/roll 127.0.0.1 - - [01/Feb/2025 10:30:59] \u0026#34;GET /roll HTTP/1.1\u0026#34; 200 - {\u0026#34;result\u0026#34;:6} $ kill %1 [1]+ Terminated flask --app dice.py run\nSembra funzionare!\n🌿 Non sprecare risorse Dopo alcune settimane frenetiche, scoprite che il vostro servizio è effettivamente utilizzato, ma non quanto vi aspettavate. Solo poche persone desiderano ottenere numeri casuali e solo poche volte al giorno; quindi sembra un po\u0026rsquo; uno spreco avere un interprete Python sempre in esecuzione che occupa diversi megabyte di memoria per uno scopo così limitato. Quindi, prepariamo un file di unità socket:\n# /etc/systemd/system/diceroll.socket [Unit] Description=Socket for diceroll service activation PartOf=diceroll.service [Socket] ListenStream=5000 NoDelay=true Backlog=128 [Install] WantedBy=sockets.target e il corrispondente file service:\n# /etc/systemd/system/diceroll.service [Unit] Description=Socket-activated dice rolling service Requires=diceroll.socket After=network.target [Service] ExecStart=/usr/bin/python3 /opt/dice_ng.py Type=simple Proviamolo; una nota importante: all\u0026rsquo;avvio deve essere avviata e abilitata solo l\u0026rsquo;unità .socket; il corrispondente file .service verrà avviato automaticamente su richiesta.\n$ systemctl daemon-reload $ systemctl enable --now diceroll.socket $ curl http://127.0.0.1:5000/roll curl: (56) Recv failure: Connection reset by peer Wow, qualcosa è andato storto \u0026#x1f914;\n🩹 Risolvere il problema C\u0026rsquo;è un problema nella nostra soluzione: all\u0026rsquo;avvio, il server cerca di mettersi in ascolto sulla connessione ma trova il socket già occupato da systemd. Dobbiamo modificare la nostra applicazione per gestire il socket aperto e passato da systemd:\nimport socket import os, sys import flask, random from werkzeug.serving import make_server app = flask.Flask(__name__) @app.route(\u0026#39;/roll\u0026#39;) def roll_dice(): return {\u0026#39;result\u0026#39;: random.randint(1, 6)} def get_systemd_socket(): \u0026#34;\u0026#34;\u0026#34;Retrieve the socket passed by systemd\u0026#34;\u0026#34;\u0026#34; listen_fds = int(os.environ.get(\u0026#39;LISTEN_FDS\u0026#39;, 0)) if listen_fds != 1: sys.stderr.write(\u0026#34;Error: systemd did not provide exactly one socket.\\n\u0026#34;) sys.exit(1) sock = socket.fromfd(3, socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) return sock if __name__ == \u0026#39;__main__\u0026#39;: sock = get_systemd_socket() server = make_server(\u0026#39;localhost\u0026#39;, 5000, app, fd=sock.fileno()) server.serve_forever() $ curl http://127.0.0.1:5000/roll {\u0026#34;result\u0026#34;:1} Ora funziona e il server si avvia su richiesta. Qualcuno potrebbe notare che l\u0026rsquo;applicazione rimane in esecuzione per sempre e non si ferma mai, quindi dopo il primo avvio resta attiva e consuma risorse, anche quando è inattiva! D\u0026rsquo;altra parte, non possiamo semplicemente avere un servizio che gestisce una singola connessione e poi si interrompe immediatamente, poiché la gestione di molte connessioni sarebbe meno efficiente e molto simile a un server inetd/CGI.\n🫳 Per favore, ti fermi? Per risolvere questo inconveniente, potremmo aggiungere alla nostra applicazione dei controlli e della logica per arrestarsi quando rimane inattiva per troppo tempo. Un effetto simile si può ottenere utilizzando l\u0026rsquo;opzione --exit-idle-time dell\u0026rsquo;utilità systemd-socket-proxyd; possiamo anche usare un timer di systemd per arrestare elegantemente la nostra applicazione dopo un periodo di tempo prestabilito. La prima soluzione è più robusta e pulita ma esula dagli scopi di questo tutorial, forse la approfondiremo in un prossimo articolo; per ora vogliamo divertirci con le funzionalità di systemd:\n# /etc/systemd/system/diceroll.service [Unit] Description=Socket-activated dice rolling service After=network.target [Service] ExecStart=/usr/bin/python3 /opt/dice_ng.py Type=simple TimeoutStartSec=1min # Timeout dopo 1 minuto di inattività (nessuna nuova connessione) # ExecStop verrà eseguito al raggiungimento di TimeoutStartSec. ExecStop=/bin/systemctl stop your-app.service [Install] WantedBy=multi-user.target ⌛ Come funziona: Il file .socket ascolta le connessioni in arrivo. Quando arriva una connessione, attiva il servizio dell\u0026rsquo;applicazione (diceroll.service). Systemd avvia il servizio dell\u0026rsquo;applicazione. Il timer TimeoutStartSec inizia a contare. Se non arrivano nuove connessioni entro il periodo TimeoutStartSec, systemd considera fallito l\u0026rsquo;avvio del servizio ed esegue il comando ExecStop, che arresta l\u0026rsquo;applicazione. \u0026#x1f44b; Ciao Molti aspetti di systemd rimangono poco conosciuti, e nuove funzionalità e capacità vengono continuamente aggiunte con ogni nuova versione. Questa esplorazione ne evidenzia solo una frazione del potenziale, e un\u0026rsquo;ulteriore indagine sulle sue funzionalità più avanzate può spesso sbloccare soluzioni ancora più eleganti ed efficienti per la gestione e l\u0026rsquo;automazione dei servizi. Che si tratti di sfruttare i timer, la socket activation o esplorare le complessità di dipendenze e target, systemd offre una ricca cassetta degli attrezzi per amministratori e sviluppatori.\n","permalink":"https://ilmanzo.github.io/it/post/systemd-socket-activated-services/","summary":"\u003ch2 id=\"-di-cosa-si-tratta\"\u003e💭 Di cosa si tratta?\u003c/h2\u003e\n\u003cp\u003eImmaginate un server web che si avvia solo quando qualcuno tenta effettivamente di accedervi. O un database che si attiva solo quando arriva una query: questa è la \u003cem\u003emagia\u003c/em\u003e della socket activation (attivazione tramite socket). Il concetto non è nuovo, poiché gli amministratori di sistema di vecchia scuola ricorderanno l\u0026rsquo;uso in passato di strumenti come \u003ca href=\"https://en.wikipedia.org/wiki/Inetd\"\u003einetd\u003c/a\u003e o \u003ca href=\"https://en.wikipedia.org/wiki/Xinetd\"\u003exinetd\u003c/a\u003e per l\u0026rsquo;attivazione dei servizi su richiesta (on-demand).\u003c/p\u003e\n\u003cp\u003ePoiché alcuni progetti interessanti come \u003ca href=\"https://cockpit-project.org/\"\u003ecockpit\u003c/a\u003e hanno già iniziato a utilizzare questa funzionalità poco conosciuta, in questo blog post vedremo le basi e cercheremo di familiarizzare con gli strumenti.\u003c/p\u003e","title":"Spiegazione della Socket Activation di systemd"},{"content":"Perché? Durante il mio lavoro quotidiano mi capita a volte di dover fare il debug di job di test openQA falliti.\nUno dei mantra del testing è riprodurre il problema e per questo compito la community di openQA ha sviluppato alcuni strumenti.\nIn pratica, ho spesso un output come questo qui sotto proveniente da alcune operazioni di clonazione dei job:\nCloning parents of sle-15-SP4-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit 1 job has been created: - sle-15-SP4-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit -\u0026gt; https://openqa.suse.de/tests/16425390 Cloning parents of sle-15-SP5-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit 1 job has been created: - sle-15-SP5-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit -\u0026gt; https://openqa.suse.de/tests/16425391 Cloning parents of sle-15-SP4-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit 1 job has been created: - sle-15-SP4-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit -\u0026gt; https://openqa.suse.de/tests/16425392 E quando voglio monitorare quei job, dovrei copiare e incollare tutti gli URL dei job e passarli come argomenti alla fantastica utilità openqa-mon, che mostrerà e mi notificherà lo stato dei job nel terminale.\n$ openqa-mon https://openqa.suse.de/tests/16425390+2 Immagina di dover monitorare 50 job openQA contemporaneamente. Copiare e incollare manualmente ogni URL dall\u0026rsquo;output della console in openqa-mon richiede tempo ed è soggetto a errori. Questo diventa rapidamente un collo di bottiglia nel mio workflow.\nInizio Sebbene openQA offra un\u0026rsquo;interfaccia web per monitorare i job, preferisco il workflow basato su terminale di openqa-mon per la sua flessibilità e le sue capacità di scripting. Tuttavia, anche con openqa-mon, raccogliere manualmente gli URL rimane un punto dolente.\nDa persona pigra, mi chiedo sempre: posso automatizzarlo? Ogni volta che mi ritrovo a fare la stessa cosa due o tre volte. Ovviamente sì. Lo faremo in Rust \u0026#x1f980;? Beh, perché no? Forse imparerò qualcosa nel processo \u0026#x1f604;\n$ cargo init oqa-jobfilter Crediti immagine a: @taryn-elliott\nIl progetto completo è disponibile su GitHub ed è sotto licenza MIT.\nDefinizione del problema Il programma dovrebbe comportarsi come un filtro per la shell, accettando l\u0026rsquo;input tramite stdin e producendo l\u0026rsquo;output tramite stdout: $ openqa-clone-job \u0026lt;myjobs\u0026gt; | oqa-jobfilter Il programma deve essere testabile: voglio svilupparlo utilizzando un processo di sviluppo Test-Driven, che mi permetta di modificarne il design e l\u0026rsquo;architettura interna pur mantenendo lo stesso comportamento L\u0026rsquo;output deve essere ordinato e pronto per essere passato così com\u0026rsquo;è a un\u0026rsquo;invocazione di openqa-mon L\u0026rsquo;output deve essere il più compatto possibile; ad esempio, quando ho ID di test consecutivi come https://openqa.suse.de/tests/1201, https://openqa.suse.de/tests/1202, https://openqa.suse.de/tests/1203, https://openqa.suse.de/tests/1204 posso semplicemente inviare 1201+3 a openqa-mon. Allo stesso modo, diversi ID di job per la stessa istanza openQA possono essere raggruppati separandoli da virgole, quindi test clonati come https://openqa.suse.de/tests/1201, https://openqa.suse.de/tests/1207, https://openqa.suse.de/tests/1210, https://openqa.suse.de/tests/1215 dovrebbero diventare openqa-mon https://openqa.suse.de 1201,1207,1210,1215 Dettagli di implementazione Il concetto di Traits in Rust è essenziale per soddisfare i requisiti #1 e #2. Questo significa che non scriveremo una funzione che richiede un parametro di un tipo specifico, ma accetteremo qualsiasi tipo che implementi quei comportamenti di Read/Write. Questo è simile alle interfacce di Go (or alle classi astratte nei linguaggi orientati agli oggetti) ed è un paradigma di programmazione molto potente.\nQuindi la nostra funzione main leggerà e scriverà da stdin/stdout, mentre la funzione di calcolo vera e propria leggerà/scriverà semplicemente da/su un lettore/scrittore \u0026ldquo;generico\u0026rdquo;. In questo modo possiamo anche testare la funzione passando input fittizi e ispezionando gli output.\npub fn process_input\u0026lt;R: Read, W: Write\u0026gt;(input: R, mut output: W) -\u0026gt; io::Result\u0026lt;()\u0026gt; { Requisito #3: L\u0026rsquo;ordinamento, la de-duplicazione e la formattazione sono gestiti da funzionalità incluse nella ricca libreria standard di Rust.\nIl requisito #4 è il più complesso: per implementare il controllo degli ID consecutivi e il raggruppamento per lo stesso dominio dobbiamo memorizzare ogni job in una struttura dati adeguata\npub struct OpenQAJob { pub domain: Domain, pub id: u32, pub consecutive_count: u32, } che a questo punto merita di essere inserita in un file sorgente separato. È un\u0026rsquo;ottima occasione per imparare come organizzare un progetto Rust e modellare i \u0026ldquo;Domain Objects\u0026rdquo;. Nota che a ogni OpenQAJob sono associate delle funzioni (molto simili a dei \u0026ldquo;metodi\u0026rdquo;).\nFunzionalità interessanti Ho cercato di utilizzare anche alcune funzionalità del linguaggio Rust:\nvalutazione delle costanti a tempo di compilazione il codice è organizzato e suddiviso in file sorgente logicamente separati il linter \u0026ldquo;clippy\u0026rdquo; è configurato per essere il più pignolo possibile commenti di documentazione: possiamo estrarre facilmente la documentazione direttamente dal codice sorgente unit testing per coprire tutti i casi e consentire un refactoring senza timori Come bonus, ho aggiunto una GitHub action per eseguire gli unit test e compilare il progetto a ogni commit+push; pronto per un ciclo di release appropriato.\nConclusioni La creazione di questo programma è stata una sessione di hacking rapida e senza troppi fronzoli, quindi ci sono sicuramente opportunità di miglioramento: se vuoi contribuire, sentiti libero di contattarmi e/o segnalare problemi (issue) o inviare pull request. Buon divertimento!\n","permalink":"https://ilmanzo.github.io/it/post/openqa-job-filter-rust/","summary":"\u003ch2 id=\"perché\"\u003ePerché?\u003c/h2\u003e\n\u003cp\u003eDurante il mio lavoro quotidiano mi capita a volte di dover fare il debug di \u003ca href=\"https://open.qa/\"\u003ejob di test openQA\u003c/a\u003e falliti.\u003c/p\u003e\n\u003cp\u003eUno dei mantra del testing è \u003ca href=\"https://www.testdevlab.com/blog/issue-reproduction-why-reproducing-bugs-matter\"\u003eriprodurre il problema\u003c/a\u003e e per questo compito la community di openQA ha \u003ca href=\"https://github.com/os-autoinst/scripts\"\u003esviluppato alcuni strumenti\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003eIn pratica, ho spesso un output come questo qui sotto proveniente da alcune operazioni di clonazione dei job:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCloning parents of sle-15-SP4-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1 job has been created:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - sle-15-SP4-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit -\u0026gt; https://openqa.suse.de/tests/16425390\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCloning parents of sle-15-SP5-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1 job has been created:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - sle-15-SP5-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit -\u0026gt; https://openqa.suse.de/tests/16425391\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCloning parents of sle-15-SP4-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e1 job has been created:\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e - sle-15-SP4-Server-DVD-Updates-x86_64-Build20250112-1-fips_ker_mode_gnome@64bit -\u0026gt; https://openqa.suse.de/tests/16425392\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eE quando voglio monitorare quei job, dovrei copiare e incollare tutti gli URL dei job e passarli come argomenti alla fantastica utilità \u003ca href=\"https://github.com/os-autoinst/openqa-mon\"\u003eopenqa-mon\u003c/a\u003e, che mostrerà e mi notificherà lo stato dei job nel terminale.\u003c/p\u003e","title":"Scrivere filtri per la shell per divertimento e profitto"},{"content":"🎄 Introduzione Dopo una SUSE hackweek davvero interessante e divertente, come ogni dicembre da qualche anno a questa parte, ho partecipato all\u0026rsquo;Advent of Code.\nPrima di tutto, desidero ringraziare Eric Wastl perché ogni anno ci regala una fantastica e indimenticabile avvent-ura.\n[fonte dell\u0026rsquo;immagine: Reddit u/edo360 ]\n✨ Cos\u0026rsquo;è l\u0026rsquo;Advent of Code? Più che un semplice conto alla rovescia per il Natale, AoC è un gioco divertente che invita gli sviluppatori di ogni età e livello ad affinare le proprie capacità di problem-solving e di programmazione. Come un calendario dell\u0026rsquo;avvento virtuale, AoC propone un nuovo enigma di programmazione ogni giorno, dal 1° al 25 dicembre. Questi rompicapi sono spesso ingannevolmente semplici a prima vista, ma si rivelano rapidamente sfide intricate che richiedono algoritmi intelligenti e codice efficiente.\nNel corso degli anni il numero di partecipanti è costantemente aumentato, con quasi 300.000 utenti che hanno completato almeno un puzzle. Quest\u0026rsquo;anno è stato davvero speciale perché è il 10° anniversario, quindi alla fine sono riuscito a completare TUTTI i puzzle e raggiungere 500 Stelle!\n🎁 Alcuni punti salienti personali Come scelta deliberata, ho risolto la maggior parte delle giornate utilizzando un mix di due linguaggi: il D Programming Language e il Crystal Programming Language. Desidero esplorarli entrambi più a fondo, e AoC sembrava il terreno di gioco ideale.\nCrystal, con la sua sintassi simile a Ruby, mi permette di scrivere codice espressivo rapidamente, il che è ideale per le sessioni mattutine di risoluzione dei puzzle. E quando le performance diventano critiche, la natura compilata di Crystal brilla davvero.\nD, d\u0026rsquo;altra parte, offre potenti strumenti di metaprogrammazione che mi permettono di sperimentare diversi approcci e creare soluzioni riutilizzabili. Inoltre, le sue funzionalità moderne e la sua combinazione di capacità ad alto e basso livello lo rendono un piacere da usare.\nNonostante siano meno conosciuti, penso che siano ottimi linguaggi da utilizzare e che dovrebbero essere più diffusi nell\u0026rsquo;ambiente IT, quindi ho colto l\u0026rsquo;occasione per sostenerli e diffonderne la conoscenza. Se siete curiosi, potete trovare la maggior parte delle soluzioni nel mio repository GitHub, ma tenete presente che questo non è inteso come codice pronto per la produzione; è scritto solo per divertimento alle 6 del mattino ed è privo di qualsiasi best practice: al contrario, è il mio momento di vacanza ed esperimento per fare qualche trucco sporco e scrivere codice conciso, quasi illeggibile di proposito\u0026hellip; Siete avvisati 😅\nSe capite l\u0026rsquo;italiano e volete sentirmi parlare dell\u0026rsquo;Advent of Code, ho avuto anche il piacere di essere ospite in una puntata del podcast dei ragazzi di Pointer Podcast 🎙️. Consigliatissimo iscriversi!\nTra tutti i 25 puzzle risolti durante il mese, posso citare:\nIl Giorno 1 come inizio offre una partenza semplice da affrontare con molti approcci diversi Il Giorno 3 perché permette di familiarizzare con le espressioni regolari e alcuni casi limite Il Giorno 6 come primo problema basato su \u0026ldquo;griglia\u0026rdquo;, facile ma con la seconda parte non banale; ricorda anche un meccanismo già visto in alcuni videogiochi Il Giorno 7, un problema semplice per migliorare la propria abilità con la ricorsione e il backtracking Il Giorno 8 e il Giorno 13 per giocare con la matematica vettoriale Il Giorno 9 per l\u0026rsquo;idea di implementare un deframmentatore di disco molto elementare Il Giorno 12 giardinaggio: misurazione del perimetro e dell\u0026rsquo;area di forme bizzarre Il Giorno 14 robot in movimento: un inaspettato colpo di scena nella seconda parte! Il Giorno 15 istruire un robot per giocare a una variante di sokoban Il Giorno 16 e il Giorno 20 puzzle di labirinti con un risvolto di \u0026ldquo;Race Condition\u0026rdquo;, in cui i giocatori possono passare attraverso alcune pareti grazie a un \u0026ldquo;glitch\u0026rdquo; Il Giorno 21 in cui persino la definizione del problema è ricorsiva: devi controllare un robot che controlla un altro robot che controlla un robot per premere dei pulsanti\u0026hellip; Il Giorno 23 e il Giorno 24 due classici problemi teorici su grafi e logica booleana Il Giorno 25 un ultimo problema facile che può essere risolto in molti modi, con un\u0026rsquo;attenzione speciale all\u0026rsquo;ottimizzazione delle performance In base ai tempi della Leaderboard, i giorni più difficili sono stati il 15, il 17, il 21 e il 24. Sono totalmente d\u0026rsquo;accordo, andate a darci un\u0026rsquo;occhiata se vi piacciono le sfide difficili 😁\n🎅 Non sei solo Una menzione speciale alla community: la cosa migliore dell\u0026rsquo;Advent of Code è far parte di un\u0026rsquo;esperienza collettiva, dove ogni giorno puoi condividere opinioni, ricevere o dare consigli, leggere meme divertenti e giocare insieme a un sacco di persone in gamba. Che si tratti di Reddit o dei tuoi amici locali, di un gruppo Telegram o di un canale Slack, condividerlo con altre persone è il vero motivo per cui è così piacevole.\nLa collaborazione stimola la creatività: affrontare l\u0026rsquo;AoC con amici o colleghi apre un mondo di intuizioni condivise e diversi approcci di problem-solving, portando a momenti di illuminazione (\u0026ldquo;aha!\u0026rdquo;) che potresti perdere da solo. Motivazione e responsabilità: sapere che altri stanno lavorando agli stessi puzzle ti mantiene motivato e impegnato, anche quando le sfide si fanno difficili. Apprendimento e condivisione delle competenze: spiegare le tue soluzioni e discutere diverse tecniche di programmazione con gli altri consolida la tua comprensione e ti espone a nuove idee. Competizione amichevole e cameratismo: una piccola competizione amichevole può essere un ottimo motivatore, e festeggiare i successi insieme rende l\u0026rsquo;esperienza ancora più gratificante. Trasformare la programmazione in un evento sociale: AoC può diventare una divertente attività sociale, sia attraverso gruppi online che sessioni di programmazione di persona con gli amici. ☃️ Considerazioni finali Che tu sia uno sviluppatore esperto o che tu stia appena iniziando il tuo viaggio nella programmazione, Advent of Code offre un\u0026rsquo;esperienza unica e gratificante. Oltre alla soddisfazione di risolvere puzzle intricati, è un\u0026rsquo;opportunità per esplorare nuovi linguaggi di programmazione, ottimizzare il tuo codice per l\u0026rsquo;efficienza e imparare da una vivace community di colleghi sviluppatori. Quindi, abbraccia lo spirito natalizio, prendi il tuo linguaggio di programmazione preferito e tuffati nell\u0026rsquo;affascinante mondo di Advent of Code. Chissà, potresti scoprire un nuovo trucco o due lungo il percorso!\nAnche se l\u0026rsquo;avvento di quest\u0026rsquo;anno è terminato, i puzzle sono ancora online: puoi provare a risolverli in qualsiasi momento.\nps: Se ti interessano Rust e l\u0026rsquo;ottimizzazione estrema delle prestazioni, ti consiglio di dare un\u0026rsquo;occhiata ad Advent of CodSpeed 🐇\n","permalink":"https://ilmanzo.github.io/it/post/advent_of_code_2024/","summary":"\u003ch2 id=\"-introduzione\"\u003e🎄 Introduzione\u003c/h2\u003e\n\u003cp\u003eDopo una \u003ca href=\"https://hackweek.opensuse.org/24/projects/hack-on-rich-terminal-user-interfaces\"\u003eSUSE hackweek\u003c/a\u003e davvero interessante e divertente, come ogni dicembre da qualche anno a questa parte, ho partecipato all\u0026rsquo;\u003ca href=\"https://adventofcode.com/\"\u003eAdvent of Code\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003ePrima di tutto, desidero ringraziare \u003ca href=\"https://was.tl/\"\u003eEric Wastl\u003c/a\u003e perché ogni anno ci regala una fantastica e indimenticabile \u003cstrong\u003eavvent\u003c/strong\u003e-ura.\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"aoc_picture\" loading=\"lazy\" src=\"/img/aoc2024.jpeg\"\u003e\n[fonte dell\u0026rsquo;immagine: \u003ca href=\"https://www.reddit.com/r/adventofcode/\"\u003eReddit\u003c/a\u003e \u003ca href=\"https://www.reddit.com/user/edo360/\"\u003eu/edo360\u003c/a\u003e ]\u003c/p\u003e\n\u003ch2 id=\"-cosè-ladvent-of-code\"\u003e✨ Cos\u0026rsquo;è l\u0026rsquo;Advent of Code?\u003c/h2\u003e\n\u003cp\u003ePiù che un semplice conto alla rovescia per il Natale, \u003cem\u003eAoC\u003c/em\u003e è un gioco divertente che invita gli sviluppatori di ogni età e livello ad affinare le proprie capacità di problem-solving e di programmazione. Come un calendario dell\u0026rsquo;avvento virtuale, AoC propone un nuovo enigma di programmazione ogni giorno, dal 1° al 25 dicembre. Questi rompicapi sono spesso ingannevolmente semplici a prima vista, ma si rivelano rapidamente sfide intricate che richiedono algoritmi intelligenti e codice efficiente.\u003c/p\u003e","title":"Resoconto dell'Advent of Code 2024"},{"content":"Introduzione Dal 2015, il GoLab è una delle conferenze più storiche e rinomate al mondo sull\u0026rsquo;ecosistema del Go Programming Language, attirando un vasto pubblico di Gopher da ogni angolo del pianeta.\nErrore mio, ho dimenticato di fare una foto alla maglietta 🤷\nNegli ultimi anni, gli organizzatori hanno ospitato alcuni dei più grandi nomi del settore, che hanno condiviso le proprie intuizioni ed esperienze con i partecipanti.\nCome prima impressione di benvenuto, la sede era uno splendido hotel nell\u0026rsquo;affascinante città di Firenze ⚜️; adoro questo posto e non c\u0026rsquo;è molto altro da aggiungere.\nScelta notevole, nel 2024 il GoLab si è tenuto nei giorni immediatamente successivi al RustLab, mentre l\u0026rsquo;anno precedente i due eventi si erano sovrapposti nella stessa location. Una scelta saggia per evitare confusione e il rischio di perdersi il proprio talk preferito!\nGrandi complimenti agli organizzatori per aver scelto di realizzare una conferenza sostenibile: piantare un albero per ogni speaker, eliminare la plastica, supportare i viaggi sostenibili e offrire un menu di pranzi e snack completamente vegetariano.\nI tre giorni del programma erano fitti, con il primo riservato a workshop approfonditi. Più di 400 persone da tutto il pianeta hanno partecipato alla conferenza, con 30 speaker selezionati tra le migliori aziende del mondo (SUSE inclusa).\nAlcuni punti salienti personali un benvenuto affollato\nEssendo riuscito a seguire circa la metà dei talk, ho la sensazione di essermene persi alcuni davvero ottimi; ecco un breve riassunto dei miei preferiti:\nGiorno uno Russ Cox ha fatto luce su un argomento controverso ma importante: la telemetria. Come il team di Go raccoglie specifiche metriche di build Russ Cox\nAlessio Greggi di SUSE ha tenuto una presentazione con demo sulla creazione automatica di profili SECCOMP utilizzando diversi strumenti, come strace e Harpoon. Alessio Greggi\nTomáš Sedláček è andato a fondo delle ragioni di design per la scelta di comunicazioni I/O sincrone o asincrone.\nRoberto Clapis ha tenuto un workshop sulla programmazione sicura e ha parlato dell\u0026rsquo;approccio difensivo, in particolare durante il parsing di dati di input complessi e sconosciuti.\nAlan Donovan ha spiegato come sono riusciti a scalare le prestazioni di gopls (il Go Language Server) di un ordine di grandezza (10x).\nPause caffè e momenti di networking!\nTeea Alarto ha parlato di un approccio pratico ed efficace all\u0026rsquo;uso dei generics (una funzionalità relativamente giovane del linguaggio Go) per scrivere codice più robusto e semplice. Ron Evans ci ha fatto viaggiare nel tempo in un\u0026rsquo;atmosfera da \u0026ldquo;Ritorno al futuro\u0026rdquo; (12 novembre, indizio indizio): il keynote includeva droni volanti alimentati da tinyGo, acquisizione video e streaming con riconoscimento facciale e un panel di \u0026ldquo;teste parlanti\u0026rdquo; di LLM malvagie sul futuro dell\u0026rsquo;umanità; date un\u0026rsquo;occhiata alla registrazione video. Alla fine di questa lunga giornata, abbiamo festeggiato il quindicesimo anno di Go con una vera festa di compleanno! 🎂\nRon Evans con il cappello di stagnola, mentre modera il panel automatizzato delle LLM\nGiorno due Camminando verso la conferenza, sulla riva dell\u0026rsquo;Arno in una soleggiata mattina d\u0026rsquo;autunno\nJosephine Winter ha iniziato in ideale continuità con il giorno precedente, mostrando il suo progetto per automatizzare la routine quotidiana del suo animale domestico utilizzando Arduino e TinyGo, con un esempio reale di apertura della porta della cuccia e rilascio del cibo per cani. Josephine appena prima di presentare il suo cane\nJan Mercl ci ha mostrato come sia possibile evitare cGo e creare una versione di sqlite in puro Go utilizzando il compilatore/transpiler da C a Go (modernc.org/ccgo/v4) e il supporto runtime che emula la libc del C (modernc.org/libc).\nDavide Imola si è lanciato in un\u0026rsquo;avventura tra le terre del Domain Driven Design.\nDavide Imola\nMichele Caci ci ha coinvolto con la sua passione per i giochi da tavolo, in particolare Ticket to Ride, usandolo come spunto per offrirci un ripasso della teoria dei grafi. giocare a Ticket to Ride in Go\nFederico Paolinelli ha fornito una panoramica degli strumenti che Go offre di serie per il testing unitario delle nostre applicazioni e ha proposto una serie di nuove tecniche per scrivere test più coerenti e comprensibili che si adattino bene a un progetto Go. fondamenti sui test in Go\nJesús Espino ha presentato un\u0026rsquo;analisi molto approfondita e dettagliata a basso livello di un binario ELF di Go, lo scopo di ciascuna sezione e come ridurre le dimensioni dei nostri binari. Takuto Nagami ha parlato della sua libreria in puro Go resigif per ridimensionare le GIF animate, ottenendo un significativo miglioramento delle prestazioni rispetto a strumenti classici come ImageMagick. ridimensionare GIF animate senza strumenti esterni\ninfine, c\u0026rsquo;è stato tempo anche per molti interessanti lightning talk\u0026hellip; Otter per lo sviluppo frontend\nCosa mi porto a casa L\u0026rsquo;equilibrio di Go tra eredità e innovazione mi ha davvero colpito. Vedere come è maturato pur rimanendo fedele ai suoi principi fondamentali è fonte di ispirazione. È una testimonianza del design del linguaggio e della community che lo spinge in avanti. Questo mi rende ancora più entusiasta di vedere dove andrà Go in futuro! Com\u0026rsquo;era Go 10 anni fa?\nLa community di Go è davvero qualcosa di speciale. Incontrare così tante persone appassionate e disponibili è stato un punto saliente. Rafforza l\u0026rsquo;idea che un linguaggio sia più di una semplice sintassi; riguarda le persone che lo usano e che costruiscono insieme cose straordinarie.\nUno dei miei maggiori apprendimenti è stato approfondire i testscript di Go. Offrono un modo così potente non solo per testare il codice, ma anche per documentare esempi e pattern di utilizzo. Sicuramente integrerò maggiormente i testscript nel mio workflow.\nI lightning talk sono stati una miniera d\u0026rsquo;oro! Il concetto di utilizzare il contesto del comando per annullare i comandi a lunga esecuzione mi ha davvero sbalordito. È una soluzione davvero elegante a un problema comune e non vedo l\u0026rsquo;ora di sperimentarla nei miei progetti.\nA presto!\n","permalink":"https://ilmanzo.github.io/it/post/golab2024/","summary":"\u003ch2 id=\"introduzione\"\u003eIntroduzione\u003c/h2\u003e\n\u003cp\u003eDal 2015, il GoLab è una delle conferenze più storiche e rinomate al mondo sull\u0026rsquo;ecosistema del \u003ca href=\"https://go.dev/\"\u003eGo Programming Language\u003c/a\u003e, attirando un vasto pubblico di Gopher da ogni angolo del pianeta.\u003c/p\u003e\n\u003cfigure\u003e\u003ca href=\"/img/golab_2024/IMG_20241113_074003.jpg\"\u003e\n    \u003cimg loading=\"lazy\" src=\"/img/golab_2024/IMG_20241113_074003.jpg\"\n         alt=\"Errore mio, ho dimenticato di fare una foto alla maglietta 🤷\" height=\"300\"/\u003e \u003c/a\u003e\u003cfigcaption\u003e\n            \u003cp\u003eErrore mio, ho dimenticato di fare una foto alla maglietta 🤷\u003c/p\u003e\n        \u003c/figcaption\u003e\n\u003c/figure\u003e\n\n\u003cp\u003eNegli ultimi anni, gli \u003ca href=\"https://www.develer.com/en/\"\u003eorganizzatori\u003c/a\u003e hanno ospitato alcuni dei più grandi nomi del settore, che hanno condiviso le proprie intuizioni ed esperienze con i partecipanti.\u003c/p\u003e","title":"Resoconto del GoLab 2024"},{"content":"👻 Introduzione Mentre il nostro post precedente si concentrava sui componenti principali dei test LTP, oggi in questa seconda parte faremo un\u0026rsquo;immersione incredibilmente profonda nelle opzioni disponibili in struct tst_test 🦇.\nIl Linux Test Project (LTP) è nato come uno sforzo collaborativo tra SGI, OSDL e Bull. Oggi vive grazie ai contributi congiunti di leader del settore come IBM, Cisco, Fujitsu, SUSE, Red Hat, Oracle e altri. La sua missione rimane chiara: fornire alla community open source test completi che verifichino l\u0026rsquo;affidabilità, la robustezza e la stabilità di Linux. 🕸️\n🍭 Meno chiacchiere, mostrami il codice La struct stessa è piuttosto ben commentata, quindi evidenzieremo le parti più importanti. Per il resto, consultate la documentazione\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 struct tst_test { unsigned int tcnt; struct tst_option *options; const char *min_kver; const char *const *supported_archs; const char *tconf_msg; unsigned int needs_tmpdir:1; unsigned int needs_root:1; unsigned int forks_child:1; unsigned int needs_device:1; unsigned int needs_checkpoints:1; unsigned int needs_overlay:1; unsigned int format_device:1; unsigned int mount_device:1; unsigned int needs_rofs:1; unsigned int child_needs_reinit:1; unsigned int runs_script:1; unsigned int needs_devfs:1; unsigned int restore_wallclock:1; unsigned int all_filesystems:1; unsigned int skip_in_lockdown:1; unsigned int skip_in_secureboot:1; unsigned int skip_in_compat:1; int needs_abi_bits; unsigned int needs_hugetlbfs:1; const char *const *skip_filesystems; unsigned long min_cpus; unsigned long min_mem_avail; unsigned long min_swap_avail; struct tst_hugepage hugepages; unsigned int taint_check; unsigned int test_variants; unsigned int dev_min_size; struct tst_fs *filesystems; const char *mntpoint; int max_runtime; void (*setup)(void); void (*cleanup)(void); void (*test)(unsigned int test_nr); void (*test_all)(void); const char *scall; int (*sample)(int clk_id, long long usec); const char *const *resource_files; const char * const *needs_drivers; const struct tst_path_val *save_restore; const struct tst_ulimit_val *ulimit; const char *const *needs_kconfigs; struct tst_buffers *bufs; struct tst_cap *caps; const struct tst_tag *tags; const char *const *needs_cmds; const enum tst_cg_ver needs_cgroup_ver; const char *const *needs_cgroup_ctrls; int needs_cgroup_nsdelegate:1; }; linea 2: questo è il numero di test contenuti nel programma. Se state utilizzando un approccio data-driven con molti casi di test in un array, questo numero dovrà essere uguale alla dimensione dell\u0026rsquo;array.\nlinea 3: un puntatore a una lista di opzioni terminata da null (TODO)\nlinea 4: una stringa che descrive la versione minima del kernel necessaria per questo test. Quando viene eseguito su una versione precedente, LTP escluderà automaticamente questo test con un messaggio appropriato.\nlinea 5: Un array terminato da NULL di architetture su cui viene eseguito il test, ad esempio {\u0026ldquo;x86_64\u0026rdquo;, \u0026ldquo;x86\u0026rdquo;, NULL}.\nlinea 6: Se impostato, il test esce con TCONF subito dopo essere entrato nella libreria di test. Questo viene utilizzato dalla macro TST_TEST_TCONF() per disabilitare i test a tempo di compilazione.\nlinee 7-23: insieme di flag booleani che abilitano comportamenti specifici di LTP. Ad esempio, se needs_tmpdir è true, LTP creerà automaticamente una directory temporanea per i dati del nostro programma.\nlinee 24-36: insieme di opzioni che controllano se il test debba essere eseguito o meno. Ad esempio, se min_cpus=2, il test non verrà eseguito su sistemi single-core.\nlinee 37-38: puntatori alle funzioni di setup and cleanup che verranno chiamate solo una volta, prima e dopo l\u0026rsquo;esecuzione del test.\nlinee 39-40: puntatori mutuamente esclusivi al codice di test vero e proprio. Il primo accetta un numero intero, utile quando si hanno molti casi di test per la stessa funzione. Se il test contiene un solo caso, si può usare il secondo.\nlinee 41-42: riservate per uso interno.\nlinea 43: Un array terminato da NULL di nomi di file che verranno copiati nella directory temporanea del test dalla directory dei file di dati di LTP.\nlinea 48: Una descrizione dei buffer protetti (guarded buffers) da allocare per il test. I buffer protetti sono buffer con una pagina \u0026ldquo;avvelenata\u0026rdquo; (poisoned page) allocata subito prima dell\u0026rsquo;inizio del buffer e un canarino (canary) subito dopo la fine del buffer. Vedere struct tst_buffers e tst_buffer_alloc() per i dettagli.\n🍬 Basta trucchi, voglio un dolcetto Per vedere l\u0026rsquo;uso reale di queste opzioni, analizziamo uno dei test. Prendiamone uno semplice come la syscall swapoff; più che al test in sé, siamo interessati all\u0026rsquo;uso di struct tst_test, ma il sorgente è piuttosto breve e leggere il codice è sempre educativo. È una delle quattro libertà essenziali del Software Libero, non è vero?\n# cat ltp/testcases/kernel/syscalls/swapoff/swapoff01.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Wipro Technologies Ltd, 2002. All Rights Reserved. */ /*\\ * [Description] * * Check that swapoff() succeeds. */ #include \u0026lt;unistd.h\u0026gt; #include \u0026lt;errno.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; #include \u0026#34;tst_test.h\u0026#34; #include \u0026#34;lapi/syscalls.h\u0026#34; #include \u0026#34;libswap.h\u0026#34; #define MNTPOINT\t\u0026#34;mntpoint\u0026#34; #define TEST_FILE\tMNTPOINT\u0026#34;/testswap\u0026#34; #define SWAP_FILE\tMNTPOINT\u0026#34;/swapfile\u0026#34; static void verify_swapoff(void) { if (tst_syscall(__NR_swapon, SWAP_FILE, 0) != 0) { tst_res(TFAIL | TERRNO, \u0026#34;Failed to turn on the swap file\u0026#34; \u0026#34;, skipping test iteration\u0026#34;); return; } TEST(tst_syscall(__NR_swapoff, SWAP_FILE)); if (TST_RET == -1) { tst_res(TFAIL | TTERRNO, \u0026#34;Failed to turn off swapfile,\u0026#34; \u0026#34; system reboot after execution of LTP \u0026#34; \u0026#34;test suite is recommended.\u0026#34;); } else { tst_res(TPASS, \u0026#34;Succeeded to turn off swapfile\u0026#34;); } } static void setup(void) { is_swap_supported(TEST_FILE); SAFE_MAKE_SWAPFILE_BLKS(SWAP_FILE, 65536); } static struct tst_test test = { .mntpoint = MNTPOINT, .mount_device = 1, .dev_min_size = 350, .all_filesystems = 1, .needs_root = 1, .test_all = verify_swapoff, .max_runtime = 60, .setup = setup }; linee 1-18: licenza standard e inclusione degli header file di LTP. linee 20-22: definizione di alcuni valori costanti che verranno utilizzati nel test. linee 24-41: la funzione di test vera e propria. Questa funzione in sostanza chiama swapon() per creare un file di swap (interrompendo in caso di fallimento), quindi tenta di disattivare lo swap su quel file verificando il risultato. linee 43-47: la funzione setup(), eseguita una sola volta prima dell\u0026rsquo;inizio del test: verifica se il sistema supporta lo swap, quindi crea un piccolo file di swap utilizzando funzioni di supporto. linea 49: la definizione dei parametri tst_test per questo test: linea 50: il test richiede un nuovo mountpoint: LTP si occuperà di crearlo e distruggerlo alla fine. linea 51: LTP formatta il dispositivo e lo monta nel mountpoint specificato sopra. linea 52: il dispositivo deve avere una dimensione minima di 350MB. linea 52: rileva tutti i filesystem supportati dal kernel e ripete automaticamente lo stesso test per TUTTI. linea 53: questo test deve essere eseguito come root. Se eseguito come utente normale, il codice di uscita sarà TCONF. linea 54: puntatore alla funzione di test vera e propria. linea 55: assegna 60 secondi per l\u0026rsquo;esecuzione di questo test. Se superati, LTP contrassegnerà automaticamente questo test con un errore. linea 56: puntatore alla funzione di setup. 🦸 Corri corri corri Eseguiamo questo test su una macchina virtuale openSUSE:\n# cd ltp/testcases/kernel/syscalls/swapoff # make swapoff01 # ./swapoff01 tst_tmpdir.c:316: TINFO: Using /tmp/LTP_swaGg5kZE as tmpdir (tmpfs filesystem) tst_device.c:96: TINFO: Found free device 0 \u0026#39;/dev/loop0\u0026#39; tst_test.c:1888: TINFO: LTP version: 20240930-44-g34e6dd2d2 tst_test.c:1892: TINFO: Tested kernel: 6.4.0-slfo.1.7-default #1 SMP PREEMPT_DYNAMIC Tue Oct 1 10:57:21 UTC 2024 (0e26fa9) x86_64 tst_test.c:1723: TINFO: Timeout per run is 0h 01m 30s tst_supported_fs_types.c:97: TINFO: Kernel supports ext2 tst_supported_fs_types.c:62: TINFO: mkfs.ext2 does exist tst_supported_fs_types.c:97: TINFO: Kernel supports ext3 tst_supported_fs_types.c:62: TINFO: mkfs.ext3 does exist tst_supported_fs_types.c:97: TINFO: Kernel supports ext4 tst_supported_fs_types.c:62: TINFO: mkfs.ext4 does exist tst_supported_fs_types.c:97: TINFO: Kernel supports xfs tst_supported_fs_types.c:58: TINFO: mkfs.xfs does not exist tst_supported_fs_types.c:97: TINFO: Kernel supports btrfs tst_supported_fs_types.c:62: TINFO: mkfs.btrfs does exist tst_supported_fs_types.c:105: TINFO: Skipping bcachefs because of FUSE blacklist tst_supported_fs_types.c:97: TINFO: Kernel supports vfat tst_supported_fs_types.c:62: TINFO: mkfs.vfat does exist tst_supported_fs_types.c:97: TINFO: Kernel supports exfat tst_supported_fs_types.c:58: TINFO: mkfs.exfat does not exist tst_supported_fs_types.c:132: TINFO: FUSE does support ntfs tst_supported_fs_types.c:62: TINFO: mkfs.ntfs does exist tst_supported_fs_types.c:97: TINFO: Kernel supports tmpfs tst_supported_fs_types.c:49: TINFO: mkfs is not needed for tmpfs tst_test.c:1821: TINFO: === Testing on ext2 === tst_test.c:1171: TINFO: Formatting /dev/loop0 with ext2 opts=\u0026#39;\u0026#39; extra opts=\u0026#39;\u0026#39; mke2fs 1.47.0 (5-Feb-2023) tst_test.c:1183: TINFO: Mounting /dev/loop0 to /tmp/LTP_swaGg5kZE/mntpoint fstyp=ext2 flags=0 libswap.c:198: TINFO: create a swapfile size of 1 megabytes (MB) tst_ioctl.c:26: TINFO: FIBMAP ioctl is supported swapoff01.c:46: TINFO: create a swapfile with 65536 block numbers swapoff01.c:39: TPASS: Succeeded to turn off swapfile tst_test.c:1821: TINFO: === Testing on ext3 === tst_test.c:1171: TINFO: Formatting /dev/loop0 with ext3 opts=\u0026#39;\u0026#39; extra opts=\u0026#39;\u0026#39; mke2fs 1.47.0 (5-Feb-2023) tst_test.c:1183: TINFO: Mounting /dev/loop0 to /tmp/LTP_swaGg5kZE/mntpoint fstyp=ext3 flags=0 libswap.c:198: TINFO: create a swapfile size of 1 megabytes (MB) tst_ioctl.c:26: TINFO: FIBMAP ioctl is supported swapoff01.c:46: TINFO: create a swapfile with 65536 block numbers swapoff01.c:39: TPASS: Succeeded to turn off swapfile tst_test.c:1821: TINFO: === Testing on ext4 === tst_test.c:1171: TINFO: Formatting /dev/loop0 with ext4 opts=\u0026#39;\u0026#39; extra opts=\u0026#39;\u0026#39; mke2fs 1.47.0 (5-Feb-2023) tst_test.c:1183: TINFO: Mounting /dev/loop0 to /tmp/LTP_swaGg5kZE/mntpoint fstyp=ext4 flags=0 libswap.c:198: TINFO: create a swapfile size of 1 megabytes (MB) tst_ioctl.c:26: TINFO: FIBMAP ioctl is supported swapoff01.c:46: TINFO: create a swapfile with 65536 block numbers swapoff01.c:39: TPASS: Succeeded to turn off swapfile tst_test.c:1821: TINFO: === Testing on btrfs === tst_test.c:1171: TINFO: Formatting /dev/loop0 with btrfs opts=\u0026#39;\u0026#39; extra opts=\u0026#39;\u0026#39; tst_test.c:1183: TINFO: Mounting /dev/loop0 to /tmp/LTP_swaGg5kZE/mntpoint fstyp=btrfs flags=0 libswap.c:198: TINFO: create a swapfile size of 1 megabytes (MB) libswap.c:43: TINFO: FS_NOCOW_FL attribute set on mntpoint/testswap tst_ioctl.c:21: TINFO: FIBMAP ioctl is NOT supported: EINVAL (22) libswap.c:128: TINFO: File \u0026#39;mntpoint/testswap\u0026#39; is not contiguous swapoff01.c:46: TINFO: create a swapfile with 65536 block numbers libswap.c:43: TINFO: FS_NOCOW_FL attribute set on mntpoint/swapfile swapoff01.c:39: TPASS: Succeeded to turn off swapfile tst_test.c:1821: TINFO: === Testing on vfat === tst_test.c:1171: TINFO: Formatting /dev/loop0 with vfat opts=\u0026#39;\u0026#39; extra opts=\u0026#39;\u0026#39; tst_test.c:1183: TINFO: Mounting /dev/loop0 to /tmp/LTP_swaGg5kZE/mntpoint fstyp=vfat flags=0 libswap.c:198: TINFO: create a swapfile size of 1 megabytes (MB) tst_ioctl.c:26: TINFO: FIBMAP ioctl is supported swapoff01.c:46: TINFO: create a swapfile with 65536 block numbers swapoff01.c:39: TPASS: Succeeded to turn off swapfile tst_test.c:1821: TINFO: === Testing on ntfs === tst_test.c:1171: TINFO: Formatting /dev/loop0 with ntfs opts=\u0026#39;\u0026#39; extra opts=\u0026#39;\u0026#39; The partition start sector was not specified for /dev/loop0 and it could not be obtained automatically. It has been set to 0. The number of sectors per track was not specified for /dev/loop0 and it could not be obtained automatically. It has been set to 0. The number of heads was not specified for /dev/loop0 and it could not be obtained automatically. It has been set to 0. To boot from a device, Windows needs the \u0026#39;partition start sector\u0026#39;, the \u0026#39;sectors per track\u0026#39; and the \u0026#39;number of heads\u0026#39; to be set. Windows will not be able to boot from this device. tst_test.c:1183: TINFO: Mounting /dev/loop0 to /tmp/LTP_swaGg5kZE/mntpoint fstyp=ntfs flags=0 tst_test.c:1183: TINFO: Trying FUSE... libswap.c:198: TINFO: create a swapfile size of 1 megabytes (MB) tst_ioctl.c:26: TINFO: FIBMAP ioctl is supported swapoff01.c:46: TINFO: create a swapfile with 65536 block numbers swapoff01.c:39: TPASS: Succeeded to turn off swapfile tst_test.c:1821: TINFO: === Testing on tmpfs === tst_test.c:1171: TINFO: Skipping mkfs for TMPFS filesystem tst_test.c:1147: TINFO: Limiting tmpfs size to 350MB tst_test.c:1183: TINFO: Mounting ltp-tmpfs to /tmp/LTP_swaGg5kZE/mntpoint fstyp=tmpfs flags=0 libswap.c:198: TINFO: create a swapfile size of 1 megabytes (MB) tst_ioctl.c:21: TINFO: FIBMAP ioctl is NOT supported: EINVAL (22) libswap.c:228: TCONF: Swapfile on tmpfs not implemented Summary: passed 6 failed 0 broken 0 skipped 1 warnings 0 Come si può notare, lo sviluppatore del test deve preoccuparsi solo di verificare la specifica caratteristica o funzionalità, mentre tutto il codice di contorno (boilerplate) è gestito dal framework LTP e dalle sue funzioni di utilità. Molto comodo!\n🎃 Conclusioni (?) Se siete interessati al progetto LTP, date un\u0026rsquo;occhiata al repository del progetto per ulteriore documentazione e per le linee guida di scrittura (Writing Guidelines); potete anche iscrivervi alla Mailing List di LTP.\nSe vi piace questo tipo di post di approfondimento e ne volete altri, o per qualsiasi altro feedback, non esitate a scrivermi un messaggio via email o su fosstodon. Buon divertimento!\n","permalink":"https://ilmanzo.github.io/it/post/followup_linux_test_project_options/","summary":"\u003ch2 id=\"-introduzione\"\u003e👻 Introduzione\u003c/h2\u003e\n\u003cp\u003eMentre il nostro \u003ca href=\"https://ilmanzo.github.io/it/post/first_steps_of_ltp_linux_test_project/\"\u003epost precedente\u003c/a\u003e si concentrava sui componenti principali dei test LTP, oggi in questa seconda parte faremo un\u0026rsquo;immersione incredibilmente profonda nelle opzioni disponibili in \u003ccode\u003estruct tst_test\u003c/code\u003e 🦇.\u003c/p\u003e\n\u003cp\u003eIl \u003ca href=\"https://github.com/linux-test-project/ltp\"\u003eLinux Test Project (LTP)\u003c/a\u003e è nato come uno sforzo collaborativo tra SGI, OSDL e Bull. Oggi vive grazie ai contributi congiunti di leader del settore come IBM, Cisco, Fujitsu, SUSE, Red Hat, Oracle e altri. La sua missione rimane chiara: fornire alla community open source test completi che verifichino l\u0026rsquo;affidabilità, la robustezza e la stabilità di Linux. 🕸️\u003c/p\u003e","title":"Linux Test Project parte 2"},{"content":"Introduzione Il Container Day italiano è una conferenza annuale organizzata da GRUSP con focus sulle tecnologie cloud native e container, sugli strumenti devops e sulle relative best practice. La sede scelta per quest\u0026rsquo;anno è stata un piacevole hotel a Bologna, un\u0026rsquo;ottima scelta e in una zona d\u0026rsquo;Italia molto accessibile (grazie ai vicini collegamenti autostradali e ferroviari).\nIl programma Intro by GrUSP Navigating the CNCF Landscape, one step at a time (Sara Trappetti, Michel Murabito) PlatformOps with Crossplane: how to build your next-gen Kubernetes-based platform (Daniele Monti) Introduction of logs in OpenTelemetry: features and opportunities (Martino Fornasa) My first monitoring with EBPF (Gabriele Santomaggio) Reimagine the Multi-Cluster (Francesco Torta, Guido Ricioppo) .Net Aspire - how to develop and forget about container (Mattia Muraro) Improve your container management with Event-Driven Ansible (Fabio Alessandro Locati) Leaving no Leaf Device Behind: at the Edge Computing frontier with Akri (Luca Barzè) Containers: the last opportunity to make reproducible AI (Marco Franzon) 👋 Conference closing Alcuni punti salienti personali Monitoraggio con eBPF: una svolta nell\u0026rsquo;osservabilità Una delle presentazioni di spicco è stata \u0026ldquo;My first monitoring with eBPF\u0026rdquo; di Gabriele Santomaggio. Questo talk ha illustrato la potenza di eBPF (extended Berkeley Packet Filter) nel rivoluzionare il modo in cui affrontiamo il monitoraggio del sistema e l\u0026rsquo;osservabilità. Punti chiave:\nLa capacità di eBPF di fornire approfondimenti dettagliati sulle operazioni a livello di kernel Come consenta un monitoraggio in tempo reale e con un sovraccarico (overhead) ridotto Applicazioni pratiche nel tuning delle prestazioni e nella sicurezza PlatformOps con Crossplane: costruire piattaforme Kubernetes di prossima generazione La presentazione di Daniele Monti su \u0026ldquo;PlatformOps with Crossplane\u0026rdquo; è stata un altro punto di forza. Ha spiegato come Crossplane stia cambiando le regole del gioco nell\u0026rsquo;ingegneria delle piattaforme basate su Kubernetes. Punti chiave:\nIl ruolo di Crossplane nell\u0026rsquo;astrarre l\u0026rsquo;infrastruttura complessa Come abiliti un approccio più dichiarativo alla gestione delle risorse multi-cloud Il potenziale per snellire i workflow DevOps .NET Aspire: semplificare lo sviluppo di container Il talk di Mattia Muraro su \u0026ldquo;.NET Aspire - how to develop and forget about containers\u0026rdquo; è stato illuminante. Ha mostrato gli ultimi sforzi di Microsoft per semplificare lo sviluppo cloud-native per gli sviluppatori .NET. Intuizioni:\nL\u0026rsquo;approccio di Aspire nell\u0026rsquo;astrarre le complessità dei container Come si integri con gli ecosistemi .NET esistenti Potenziale impatto sulla produttività degli sviluppatori e sulla scalabilità delle applicazioni Le frontiere dell\u0026rsquo;Edge Computing con Akri La presentazione di Luca Barzè, \u0026ldquo;Leaving no Leaf Device Behind: at the Edge Computing frontier with Akri\u0026rdquo;, è stata particolarmente intrigante. Ha evidenziato la crescente importanza dell\u0026rsquo;edge computing nell\u0026rsquo;IoT e nei sistemi distribuiti. Punti chiave:\nIl ruolo di Akri nella scoperta e nell\u0026rsquo;utilizzo dei dispositivi edge Come colmi il divario tra Kubernetes e l\u0026rsquo;edge Potenziali applicazioni nell\u0026rsquo;IoT, nell\u0026rsquo;automazione industriale e altro ancora Conclusioni Sebbene le presentazioni tecniche siano state di valore inestimabile, e la varietà degli argomenti trattati abbia messo in evidenza il ritmo incalzante dell\u0026rsquo;innovazione nelle tecnologie cloud native, la conferenza ha offerto molto più di un semplice apprendimento strutturato. È stata un\u0026rsquo;ottima occasione di networking e di scambio di conoscenze. Ho avuto la possibilità di ritrovare volti noti del settore e di incontrare nuovi professionisti del campo. Sia gli speaker che i partecipanti hanno mostrato una passione e una competenza incredibili. Queste interazioni personali e i momenti condivisi di scoperta sono stati, per molti versi, il cuore dell\u0026rsquo;esperienza della conferenza.\nInfine, un plauso agli organizzatori per il sistema interattivo di codici QR: ogni presentazione includeva un codice QR che rimandava a un sito web dedicato per domande e feedback in tempo reale. Questo approccio innovativo ha snellito le sessioni di domande e risposte (Q\u0026amp;A), sia per i partecipanti in presenza che da remoto, migliorando il colvolgimento del pubblico e assecondando le preferenze per la comunicazione scritta, dimostrando un uso efficace della tecnologia da parte della conferenza per migliorare i risultati dell\u0026rsquo;apprendimento.\nIn particolare, SUSE era uno degli sponsor, quindi ho avuto la possibilità di incontrare alcuni colleghi allo stand aziendale 🤓\n","permalink":"https://ilmanzo.github.io/it/post/containerday2024/","summary":"\u003ch2 id=\"introduzione\"\u003eIntroduzione\u003c/h2\u003e\n\u003cp\u003eIl \u003ca href=\"https://2024.containerday.it/index.html\"\u003eContainer Day\u003c/a\u003e italiano è una conferenza annuale organizzata da \u003ca href=\"https://www.grusp.org/en/\"\u003eGRUSP\u003c/a\u003e con focus sulle tecnologie cloud native e container, sugli strumenti devops e sulle relative best practice. La sede scelta per quest\u0026rsquo;anno è stata un \u003ca href=\"https://www.savoia.eu/\"\u003epiacevole hotel a Bologna\u003c/a\u003e, un\u0026rsquo;ottima scelta e in una zona d\u0026rsquo;Italia molto accessibile (grazie ai vicini collegamenti autostradali e ferroviari).\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"badge\" loading=\"lazy\" src=\"/img/containerday_2024/IMG_20241010_202216.jpg\"\u003e\u003c/p\u003e\n\u003ch2 id=\"il-programma\"\u003eIl programma\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eIntro by GrUSP\u003c/li\u003e\n\u003cli\u003eNavigating the CNCF Landscape, one step at a time (\u003ca href=\"https://github.com/SaraTrap\"\u003eSara Trappetti\u003c/a\u003e, \u003ca href=\"https://github.com/akelity\"\u003eMichel Murabito\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003ePlatformOps with Crossplane: how to build your next-gen Kubernetes-based platform (\u003ca href=\"https://github.com/Monska85\"\u003eDaniele Monti\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003eIntroduction of logs in OpenTelemetry: features and opportunities (\u003ca href=\"https://www.linkedin.com/in/fornasa/\"\u003eMartino Fornasa\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003eMy first monitoring with EBPF (\u003ca href=\"https://github.com/Gsantomaggio/\"\u003eGabriele Santomaggio\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003eReimagine the Multi-Cluster (\u003ca href=\"https://github.com/fra98\"\u003eFrancesco Torta\u003c/a\u003e, \u003ca href=\"https://github.com/guidonguido\"\u003eGuido Ricioppo\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e.Net Aspire - how to develop and forget about container (\u003ca href=\"https://github.com/mattiamuraro\"\u003eMattia Muraro\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003eImprove your container management with Event-Driven Ansible (\u003ca href=\"https://fale.io/\"\u003eFabio Alessandro Locati\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003eLeaving no Leaf Device Behind: at the Edge Computing frontier with Akri (\u003ca href=\"https://www.linkedin.com/in/barze/\"\u003eLuca Barzè\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003eContainers: the last opportunity to make reproducible AI (\u003ca href=\"https://github.com/mfranzon\"\u003eMarco Franzon\u003c/a\u003e)\u003c/li\u003e\n\u003cli\u003e👋 Conference closing\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cimg alt=\"foundations\" loading=\"lazy\" src=\"/img/containerday_2024/IMG_20241010_103454.jpg\"\u003e\u003c/p\u003e","title":"Resoconto del ContainerDay 2024"},{"content":"👣 Introduzione Gli strumenti di tracciamento sono piuttosto popolari nell\u0026rsquo;ecosistema Unix/Linux; ad esempio, nello userspace abbiamo ltrace per tracciare le chiamate di libreria dei programmi e strace per andare più a fondo e ispezionare l\u0026rsquo;uso delle syscall.\nUna delle molte caratteristiche che il kernel Linux offre dal 2008 (poi evolutasi nel tempo) è ftrace, che permette diversi tipi di tracciamento a runtime. Sebbene non sia flessibile come la tecnologia eBPF, può rivelarsi utile in alcune occasioni e non richiede un vero e proprio linguaggio di programmazione.\nFoto di Karolina Kaboompics\n🐤 I concetti base Prima di tutto, assicuriamoci che il vostro kernel sia compilato con l\u0026rsquo;opzione CONFIG_FTRACE e che il virtual filesystem di tracciamento sia montato:\n# mount | grep tracefs tracefs on /sys/kernel/tracing type tracefs (rw,nosuid,nodev,noexec,relatime) così da poter ispezionare la sua comoda interfaccia a livello utente.\n# cd /sys/kernel/tracing # cat current_tracer nop # cat README tracing mini-HOWTO: # echo 0 \u0026gt; tracing_on : quick way to disable tracing # echo 1 \u0026gt; tracing_on : quick way to re-enable tracing Important files: trace - The static contents of the buffer To clear the buffer write into this file: echo \u0026gt; trace trace_pipe - A consuming read to see the contents of the buffer current_tracer - function and latency tracers available_tracers - list of configured tracers for current_tracer error_log - error log for failed commands (that support it) buffer_size_kb - view and modify size of per cpu buffer buffer_total_size_kb - view total size of all cpu buffers Ci sono un sacco di cose qui; abbiamo persino un fantastico README con un po\u0026rsquo; di documentazione, ma procediamo un passo alla volta. Il file (virtuale) current_tracer contiene nop, il che significa che nessun tracciatore è abilitato. Dobbiamo modificarlo per poter tracciare qualcosa, e abbiamo una lunga lista di scelte:\n🤔 Cosa posso fare? # cat /sys/kernel/tracing/available_tracers timerlat osnoise blk function_graph wakeup_dl wakeup_rt wakeup function nop tracciatori di funzioni: function, function_graph tracciatori di latenza: wakeup_dl, wakeup_rt, irqsoff, wakeup, timerlat tracciatori di I/O: blk IRQ/NMI: osnoise, hwlat Per abilitare un tracciatore, dobbiamo solo scrivere il suo nome in current_tracer:\n# echo function \u0026gt; current_tracer A questo punto è possibile attivare il tracciamento con echo 1 \u0026gt; tracing_on e iniziare a leggere dal file virtuale trace (che contiene il contenuto del trace buffer) o da trace_pipe (che trasmette i dati di tracciamento in streaming mentre li leggiamo).\n🔧 Uno strumento migliore Se interagire direttamente con il filesystem può risultare un po\u0026rsquo; scomodo, esiste un pratico strumento a riga di comando creato da Steven Rostedt chiamato trace-cmd:\n$ sudo zypper in trace-cmd Loading repository data... Reading installed packages... Resolving package dependencies... The following 3 NEW packages are going to be installed: libtraceevent1 libtracefs1 trace-cmd 3 new packages to install. Vediamo alcuni esempi di utilizzo:\n# trace-cmd start -p function avvia il tracciamento con il tracciatore function (mostra le chiamate di funzione); una volta avviato, potete visualizzare i dati con\n# trace-cmd show # tracer: function # # entries-in-buffer/entries-written: 51265/8953395 #P:1 # # _-----=\u0026gt; irqs-off/BH-disabled # / _----=\u0026gt; need-resched # | / _---=\u0026gt; hardirq/softirq # || / _--=\u0026gt; preempt-depth # ||| / _-=\u0026gt; migrate-disable # |||| / delay # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | sshd-session-3242 [000] d..3. 5838.785487: finish_task_switch.isra.0 \u0026lt;-__schedule sshd-session-3242 [000] d..3. 5838.785488: _raw_spin_unlock \u0026lt;-finish_task_switch.isra.0 sshd-session-3242 [000] ...1. 5838.785489: __fdget \u0026lt;-do_sys_poll sshd-session-3242 [000] ...1. 5838.785490: sock_poll \u0026lt;-do_sys_poll sshd-session-3242 [000] ...1. 5838.785490: tcp_poll \u0026lt;-sock_poll sshd-session-3242 [000] ...1. 5838.785491: tcp_stream_memory_free \u0026lt;-tcp_poll sshd-session-3242 [000] ...1. 5838.785492: __fdget \u0026lt;-do_sys_poll sshd-session-3242 [000] ...1. 5838.785492: sock_poll \u0026lt;-do_sys_poll sshd-session-3242 [000] ...1. 5838.785493: tcp_poll \u0026lt;-sock_poll sshd-session-3242 [000] ...1. 5838.785493: tcp_stream_memory_free \u0026lt;-tcp_poll sshd-session-3242 [000] ...1. 5838.785494: __fdget \u0026lt;-do_sys_poll sshd-session-3242 [000] ...1. 5838.785495: tty_poll \u0026lt;-do_sys_poll sshd-session-3242 [000] ...1. 5838.785495: tty_ldisc_ref_wait \u0026lt;-tty_poll sshd-session-3242 [000] ...1. 5838.785496: ldsem_down_read \u0026lt;-tty_ldisc_ref_wait sshd-session-3242 [000] ...1. 5838.785496: __cond_resched \u0026lt;-ldsem_down_read sshd-session-3242 [000] ...1. 5838.785497: n_tty_poll \u0026lt;-tty_poll sshd-session-3242 [000] ...1. 5838.785500: tty_buffer_flush_work \u0026lt;-n_tty_poll sshd-session-3242 [000] ...1. 5838.785500: flush_work \u0026lt;-n_tty_poll sshd-session-3242 [000] ...1. 5838.785501: __cond_resched \u0026lt;-flush_work sshd-session-3242 [000] ...1. 5838.785501: __flush_work \u0026lt;-n_tty_poll sshd-session-3242 [000] ...1. 5838.785502: __rcu_read_lock \u0026lt;-__flush_work sshd-session-3242 [000] ...1. 5838.785502: _raw_spin_lock_irq \u0026lt;-__flush_work sshd-session-3242 [000] d..2. 5838.785505: _raw_spin_unlock_irq \u0026lt;-__flush_work sshd-session-3242 [000] ...1. 5838.785506: __rcu_read_unlock \u0026lt;-__flush_work sshd-session-3242 [000] ...1. 5838.785506: tty_hung_up_p \u0026lt;-n_tty_poll sshd-session-3242 [000] ...1. 5838.785507: mutex_is_locked \u0026lt;-n_tty_poll sshd-session-3242 [000] ...1. 5838.785507: tty_chars_in_buffer \u0026lt;-n_tty_poll sshd-session-3242 [000] ...1. 5838.785508: tty_write_room \u0026lt;-n_tty_poll sshd-session-3242 [000] ...1. 5838.785508: pty_write_room \u0026lt;-n_tty_poll sshd-session-3242 [000] ...1. 5838.785509: tty_buffer_space_avail \u0026lt;-n_tty_poll E per fermarlo usare # trace-cmd stop; potete anche svuotare il buffer con # trace-cmd clear -a o eseguire entrambe le operazioni con un semplice # trace-cmd reset.\nUn esempio con un tracciatore diverso:\n# trace-cmd start -p function_graph --max-graph-depth 5 Avvia il tracciamento di tutte le funzioni chiamate (fino a 5 livelli di profondità). Attenzione: può produrre un\u0026rsquo;enorme quantità di dati:\n# trace-cmd show # tracer: function_graph # # CPU DURATION FUNCTION CALLS # | | | | | | | ------------------------------------------ 0) sshd-se-3242 =\u0026gt; kworker-3680 ------------------------------------------ 0) | finish_task_switch.isra.0() { 0) 0.621 us | _raw_spin_unlock(); 0) 1.884 us | } 0) ! 200.114 us | } /* __cond_resched */ 0) 0.601 us | mutex_unlock(); 0) ! 225.651 us | } /* flush_to_ldisc */ 0) 0.591 us | __cond_resched(); 0) 0.592 us | _raw_spin_lock_irq(); 0) 0.630 us | pwq_dec_nr_in_flight(); 0) ! 233.837 us | } /* process_one_work */ 0) | process_one_work() { 0) 0.591 us | kick_pool(); 0) 0.591 us | set_work_pool_and_clear_pending(); 0) 0.591 us | _raw_spin_unlock_irq(); 0) | wq_barrier_func() { 0) | complete() { 0) 0.601 us | _raw_spin_lock_irqsave(); 0) | try_to_wake_up() { 0) 0.591 us | _raw_spin_lock_irqsave(); 0) 0.611 us | ttwu_queue_wakelist(); 0) 0.861 us | raw_spin_rq_lock_nested(); 0) 0.711 us | update_rq_clock(); 0) 6.762 us | ttwu_do_activate(); 0) 0.591 us | _raw_spin_unlock(); 0) 0.611 us | _raw_spin_unlock_irqrestore(); 0) + 15.138 us | } 0) 0.611 us | _raw_spin_unlock_irqrestore(); 0) + 18.515 us | } 0) + 19.627 us | } 0) 0.601 us | __cond_resched(); 0) 0.601 us | _raw_spin_lock_irq(); 0) 0.601 us | pwq_dec_nr_in_flight(); 0) + 27.621 us | } 0) 0.622 us | worker_enter_idle(); 0) 0.592 us | _raw_spin_unlock_irq(); 0) | schedule() { 0) | wq_worker_sleeping() { 0) 0.611 us | kthread_data(); 0) 1.763 us | } 0) 0.621 us | rcu_note_context_switch(); 0) | raw_spin_rq_lock_nested() { 0) 0.590 us | _raw_spin_lock(); 0) 1.694 us | } 0) 0.712 us | update_rq_clock(); 0) | dequeue_task() { Mostrerà una visualizzazione ordinata delle chiamate di funzione nidificate che avvengono nel kernel, con i tempi in microsecondi a fianco.\n📼 Registrazione e filtraggio Questo strumento può funzionare anche \u0026ldquo;registrando\u0026rdquo; in un file di dati tutti i punti di traccia raccolti, dopodiché possiamo usare la stessa utilità o altre per ispezionare i dati. Questo è particolarmente utile per eventi rari o se avete bisogno di fare il debug di problemi specifici che sembrano verificarsi in modo casuale.\n# trace-cmd record avvierà il tracciamento scrivendo i dati in un file (chiamato di default trace.dat). Dopo aver arrestato la traccia, potrete visualizzare i dati con\n# trace-cmd report Un filtro sull\u0026rsquo;evento irq_handler e sulla funzione do_IRQ mostrerà quanto tempo richiede l\u0026rsquo;IRQ nel kernel:\n# trace-cmd record -p function_graph -l do_IRQ -e irq_handler_entry sleep 10 # trace-cmd report | grep irq_handler_entry -A 2 sleep-4253 [000] 7340.590340: irq_handler_entry: irq=27 name=virtio2-input.0 sleep-4253 [000] 7340.590340: funcgraph_entry: 4.438 us | vring_interrupt(); sleep-4253 [000] 7340.590345: funcgraph_exit: 6.201 us | } -- sleep-4253 [000] 7340.590767: irq_handler_entry: irq=28 name=virtio2-output.0 sleep-4253 [000] 7340.590769: funcgraph_exit: 3.136 us | } sleep-4253 [000] 7340.590769: funcgraph_entry: 0.270 us | _raw_spin_unlock(); -- \u0026lt;idle\u0026gt;-0 [000] 7340.610004: irq_handler_entry: irq=27 name=virtio2-input.0 \u0026lt;idle\u0026gt;-0 [000] 7340.610011: funcgraph_exit: 8.436 us | } \u0026lt;idle\u0026gt;-0 [000] 7340.610011: funcgraph_entry: 1.031 us | add_interrupt_randomness(); -- sshd-session-3242 [000] 7340.610566: irq_handler_entry: irq=28 name=virtio2-output.0 sshd-session-3242 [000] 7340.610571: funcgraph_exit: 8.456 us | } sshd-session-3242 [000] 7340.610572: funcgraph_entry: + 11.051 us | irq_exit_rcu(); Per visualizzare tutte le operazioni di allocazione di memoria (del kernel) inferiori a 512 byte, possiamo filtrare sull\u0026rsquo;evento kmalloc e su un campo specifico:\n# trace-cmd record -e kmem:kmalloc -f \u0026#39;bytes_req \u0026lt; 512\u0026#39; sshd-session-3242 [000] 8115.304880: kmalloc: call_site=virtqueue_add_split+0xa9 ptr=0xffff95c7d458d300 bytes_req=32 bytes_alloc=32 gfp_flags=0x820 node=-1 accounted=false sshd-session-3242 [000] 8115.325365: kmalloc: call_site=virtqueue_add_split+0xa9 ptr=0xffff95c7d458d1c0 bytes_req=32 bytes_alloc=32 gfp_flags=0x820 node=-1 accounted=false sshd-session-3242 [000] 8115.344938: kmalloc: call_site=virtqueue_add_split+0xa9 ptr=0xffff95c7d458d1c0 bytes_req=32 bytes_alloc=32 gfp_flags=0x820 node=-1 accounted=false sshd-session-3242 [000] 8115.364728: kmalloc: call_site=virtqueue_add_split+0xa9 ptr=0xffff95c7d458d1c0 bytes_req=32 bytes_alloc=32 gfp_flags=0x820 node=-1 accounted=false sshd-session-3242 [000] 8115.385613: kmalloc: call_site=virtqueue_add_split+0xa9 ptr=0xffff95c7d458d1c0 bytes_req=32 bytes_alloc=32 gfp_flags=0x820 node=-1 accounted=false sshd-session-3242 [000] 8115.405250: kmalloc: call_site=virtqueue_add_split+0xa9 ptr=0xffff95c7d458d1c0 bytes_req=32 bytes_alloc=32 gfp_flags=0x820 node=-1 accounted=false sshd-session-3242 [000] 8115.426718: kmalloc: call_site=virtqueue_add_split+0xa9 ptr=0xffff95c7d458d1c0 bytes_req=32 bytes_alloc=32 gfp_flags=0x820 node=-1 accounted=false Per ottenere tutte le funzioni/eventi/tracciatori disponibili e così via, potete usare le opzioni di list.\n# trace-cmd list -h trace-cmd version 3.2.0 (3.2.0) usage: trace-cmd list [-e [regex]][-t][-o][-f [regex]] -e list available events -F show event format --full show the print fmt with -F -R show event triggers -l show event filters -t list available tracers -o list available options -f [regex] list available functions to filter on -P list loaded plugin files (by path) -O list plugin options -B list defined buffer instances -C list the defined clocks (and active one) -c list the supported trace file compression algorithms 🎩 Alcuni trucchi e suggerimenti Invece di analizzare l\u0026rsquo;intero sistema, potreste voler tracciare le attività del kernel relative solo a uno specifico processo (PID), utilizzando l\u0026rsquo;opzione -P:\n# trace-cmd record -p function -P 174 Su sistemi con pochissimo spazio su disco (come le schede embedded), non è facile memorizzare file di traccia di grandi dimensioni per analizzarli in seguito. trace-cmd può inviare la traccia in remoto su una rete. Basta avviarlo sull\u0026rsquo;host con l\u0026rsquo;opzione listen port:\n# trace-cmd listen -p 12345 -D e poi sul dispositivo potete \u0026ldquo;salvare\u0026rdquo; eventi e dati sul peer remoto:\n# trace-cmd record -N mypc.local.net:12345 [tracing options] Un trucco per gli sviluppatori di moduli: tracciate solo uno specifico modulo del kernel cercando funzioni che terminano con ] (output parziale):\n# lsmod | grep thinkpad thinkpad_acpi 196608 0 platform_profile 12288 1 thinkpad_acpi sparse_keymap 12288 1 thinkpad_acpi rfkill 40960 9 bluetooth,thinkpad_acpi,cfg80211 snd 159744 57 snd_ctl_led,snd_hda_codec_generic,snd_seq,snd_seq_device,snd_hda_codec_hdmi,snd_hwdep,snd_hda_intel,snd_usb_audio,snd_usbmidi_lib,snd_hda_codec,snd_hda_codec_realtek,snd_sof,snd_timer,snd_compress,thinkpad_acpi,snd_soc_core,snd_ump,snd_pcm,snd_rawmidi video 81920 2 thinkpad_acpi,amdgpu battery 28672 1 thinkpad_acpi # trace-cmd list -f | grep \u0026#39;thinkpad_acpi]$ | grep bluetooh\u0026#39; bluetooth_attr_is_visible [thinkpad_acpi] bluetooth_set_status [thinkpad_acpi] bluetooth_get_status [thinkpad_acpi] bluetooth_exit [thinkpad_acpi] bluetooth_shutdown [thinkpad_acpi] bluetooth_enable_store [thinkpad_acpi] bluetooth_enable_show [thinkpad_acpi] bluetooth_write [thinkpad_acpi] bluetooth_read [thinkpad_acpi] e vedere quali utili funzioni possiamo mettere sotto traccia, o verificare come si comporta il nostro modulo appena sviluppato.\n🏄‍♂️ Conclusione e riferimenti: Se vi occupate di sviluppo del kernel o siete semplicemente curiosi di sapere come funzionano i meccanismi interni di Linux, potete fare affidamento su questa utile funzionalità per ottenere interessanti approfondimenti sul vostro sistema. Lascio qui alcuni altri siti web e post di blog che parlano di ftrace:\nla documentazione ufficiale del Kernel Un ottimo Blog Post di Sergio Prado Due post su opensource.com di Gaurav Kamathe: 1 e 2 Un (vecchio ma prezioso) articolo LWN Se la cosa vi interessa quanto a me, non esitate a lasciarmi commenti o feedback e sarò felice di fare un approfondimento 😉\nGrazie per la lettura e Happy hacking!\n","permalink":"https://ilmanzo.github.io/it/post/kernel_ftrace/","summary":"\u003ch2 id=\"-introduzione\"\u003e👣 Introduzione\u003c/h2\u003e\n\u003cp\u003eGli strumenti di tracciamento sono piuttosto popolari nell\u0026rsquo;ecosistema Unix/Linux; ad esempio, nello userspace abbiamo \u003ca href=\"https://man7.org/linux/man-pages/man1/ltrace.1.html\"\u003eltrace\u003c/a\u003e per tracciare le chiamate di libreria dei programmi e \u003ca href=\"https://en.wikipedia.org/wiki/Strace\"\u003estrace\u003c/a\u003e per andare più a fondo e ispezionare l\u0026rsquo;uso delle syscall.\u003c/p\u003e\n\u003cp\u003eUna delle molte caratteristiche che il kernel Linux offre dal 2008 (poi evolutasi nel tempo) è \u003ca href=\"https://www.kernel.org/doc/html/latest/trace/ftrace.html\"\u003eftrace\u003c/a\u003e, che permette diversi tipi di tracciamento a runtime. Sebbene non sia flessibile come la tecnologia \u003ca href=\"https://ebpf.io/\"\u003eeBPF\u003c/a\u003e, può rivelarsi utile in alcune occasioni e non richiede un vero e proprio linguaggio di programmazione.\u003c/p\u003e","title":"Il ftrace del kernel Linux"},{"content":"💾 Introduzione Ho appena trovato una scheda SD molto economica su un negozio online e, sapendo che ci sono diversi falsi in circolazione, ho voluto verificare rapidamente se le sue dimensioni e la sua velocità rispettano le specifiche.\nNota: dopo la prima pubblicazione, un gentile lettore mi ha segnalato che gli strumenti F3 (The F3 tools) sono perfetti per questo scopo, ed è vero; se volete seguire una strada manuale e imparare qualcosa lungo il percorso, continuate a leggere\u0026hellip;\n✍️ Il test di scrittura Cosa verificheremo? Ad esempio, voglio riempire l\u0026rsquo;intero spazio con molti file (grandi e/o piccoli) e vedere se riesco a rileggerli. Dato che la mia scheda dovrebbe essere da 32GB, posso creare almeno 30 file da 1GB ciascuno:\nmmcblk0 179:0 0 30.8G 0 disk └─mmcblk0p1 179:1 0 30.8G 0 part /run/media/andrea/9016-4EF8 (nota: mmcblk0 è il nome del dispositivo per la scheda SD)\ncreiamo uno script che generi molti file, ne calcoli il checksum e li sposti sulla MicroSD:\n#!/bin/sh cd /tmp DESTDIR=/run/media/andrea/9016-4EF8 # the mount path of the microsd card for n in $(seq -w 30); do dd if=/dev/random of=bigfile$n bs=1M count=1024 status=none sha256sum bigfile$n | tee -a checksums.txt time mv bigfile$n $DESTDIR done sync eseguendo lo script, iniziamo a raccogliere l\u0026rsquo;output:\n3cbcc7583c68115996f22745b37c02bd13b9df8d164c212883af77924bcbf113 bigfile01 real 0m42,415s user 0m0,000s sys 0m1,984s 005ac6aab0d0c4f08c9813fbe8f6baa6d2c4f8be41646344fbabcb9af313dc89 bigfile02 real 0m41,676s user 0m0,004s sys 0m1,790s 8cf990ba2e3df87db55adca975f6cc24a89e915a72dc7be2bf7be6ad0cedde47 bigfile03 real 0m41,541s user 0m0,004s sys 0m1,631s 2f7653161e4c2679116042598fe0298fae8fe02ada9bb72526037bcd16247fee bigfile04 real 0m42,309s user 0m0,000s sys 0m1,246s 5e350cf51aba24490b4fb31bef4766ad165790b8fe510b8467668a598ef80620 bigfile05 real 0m40,725s user 0m0,000s sys 0m1,661s a035ddd2161119249e5841c45036dcf5e29c8c19c16506ebf964e03baab1e7a9 bigfile06 real 0m41,860s user 0m0,000s sys 0m1,190s b147d9de194fbca4499a9af77d23fe7aaedd55bacc3c9f0edd5c211f96653895 bigfile07 real 0m40,929s user 0m0,000s sys 0m1,744s 74e0b502a6ee6f020bb5c9acded9ace276ea28d15a6ff11e7101d5017c0dcfdc bigfile08 real 0m41,350s user 0m0,000s sys 0m1,614s [and so on] Si noti che, grazie al comando tee, il sha256sum di ogni file viene comodamente salvato in un file checksums.txt per un uso successivo.\nNon è una misurazione propriamente scientifica, ma possiamo notare che scrivere circa 1GB su questa scheda richiede poco più di 40 secondi, il che significa che la velocità di trasferimento è leggermente inferiore a ~25 MB/s (che la qualifica come \u0026ldquo;High Speed\u0026rdquo;). Poiché questa specifica scheda era pubblicizzata come UHS-1, possiamo dire che supera il test di velocità in scrittura.\nSe vogliamo \u0026ldquo;stressare\u0026rdquo; un po\u0026rsquo; il supporto, possiamo ripetere lo script più volte finché non lo riteniamo opportuno. Ora possiamo espellere la scheda, attendendo che le ultime scritture vengano completate; successivamente potremo verificare facilmente se il contenuto è lo stesso una volta riletto.\n👀 Il test di lettura Il programma sha256sum ha una speciale e comoda opzione -c o che accetta un file contenente le coppie di checksum e nome del file, che è esattamente ciò che abbiamo generato nel passaggio precedente:\nDESTDIR=/run/media/andrea/9016-4EF8 # the mount path of the microsd card cd $DESTDIR ; time sha256sum -c /tmp/checksums.txt bigfile01: OK bigfile02: OK bigfile03: OK bigfile04: OK bigfile05: OK bigfile06: OK [...] bigfile22: OK bigfile23: OK bigfile24: OK bigfile25: FAILED bigfile26: OK bigfile27: OK bigfile28: OK bigfile29: OK bigfile30: OK sha256sum: WARNING: 1 computed checksum did NOT match sha256sum -c /tmp/checksums.txt 154,26s user 23,13s system 49% cpu 5:55,02 total La velocità di lettura misurata è di circa 192 MB/s, anche se la cache del filesystem gioca un ruolo importante qui.\n⚠️ Conclusioni La parte interessante del test finale è che alcuni file hanno fallito il controllo del checksum; questo potrebbe essere sufficiente per considerare questa scheda non affidabile per l\u0026rsquo;uso in produzione. In breve: fate attenzione alle schede MicroSD economiche che acquistate, e assicuratevi di mantenere sempre un approccio orientato al testing! ✅\n","permalink":"https://ilmanzo.github.io/it/post/testing_a_microsd_card/","summary":"\u003ch2 id=\"-introduzione\"\u003e💾 Introduzione\u003c/h2\u003e\n\u003cp\u003eHo appena trovato una scheda SD \u003cem\u003emolto\u003c/em\u003e economica su un negozio online e, sapendo che \u003ca href=\"https://iboysoft.com/sd-card-recovery/fake-sd-card.html\"\u003eci sono diversi falsi in circolazione\u003c/a\u003e, ho voluto verificare rapidamente se le sue dimensioni e la sua velocità rispettano le specifiche.\u003c/p\u003e\n\u003cp\u003eNota: dopo la prima pubblicazione, un \u003ca href=\"https://www.foxyhole.io/@amreo\"\u003egentile lettore\u003c/a\u003e mi ha segnalato che gli \u003ca href=\"https://fight-flash-fraud.readthedocs.io/en/latest/introduction.html\"\u003estrumenti F3 (The F3 tools)\u003c/a\u003e sono perfetti per questo scopo, ed è vero; se volete seguire una strada manuale e imparare qualcosa lungo il percorso, continuate a leggere\u0026hellip;\u003c/p\u003e","title":"Testare la qualità di una scheda MicroSD economica"},{"content":"🔐 Introduzione Come sysadmin esperti, avrete probabilmente familiarità con il tradizionale approccio \u0026ldquo;tutto o niente\u0026rdquo;: se una shell o un processo è in esecuzione con UID==0, può fare quasi tutto sul sistema; al contrario, un processo utente comune è limitato in vari modi: tipicamente non può aprire socket RAW, non può fare il bind di porte \u0026ldquo;privilegiate\u0026rdquo; inferiori alla 1024, non può cambiare la proprietà di un file (chown) e così via.\nLe Linux capabilities sono una funzionalità, introdotta gradualmente a partire dal kernel 2.2, che permette un controllo più granulare sulle operazioni privilegiate, superando la tradizionale distinzione binaria tra root e non-root. Proprio come usando sudo possiamo eseguire comandi specifici come un altro utente (anche root) senza diventare permanentemente quell\u0026rsquo;utente, utilizzando le capabilities possiamo concedere a un programma solo determinati privilegi senza doverlo eseguire come root.\nCrediti immagine a: @realtoughcandy\n🧩 Cosa sono? L\u0026rsquo;idea è semplice: suddividere tutte le possibili chiamate privilegiate del kernel in gruppi di funzionalità correlate, in modo da poter assegnare ai processi solo il sottoinsieme di cui hanno effettivamente bisogno. Di conseguenza, le chiamate del kernel sono state suddivise in qualche dozzina di categorie diverse, con ottimo successo.\nIl kernel Linux implementa una moltitudine di questi permessi microscopici. Alcune delle capabilities più comunemente utilizzate sono:\nCAP_SYS_ADMIN: Consente una vasta gamma di operazioni. Questa capability dovrebbe essere evitata a favore di capabilities più specifiche. CAP_CHOWN: Consente modifiche allo User ID e al Group ID dei file CAP_DAC_READ_SEARCH: Consente di bypassare i controlli di lettura dei file e di lettura/esecuzione delle directory. Un programma con questa capability può essere utilizzato per leggere qualsiasi file sul sistema. CAP_DAC_OVERRIDE: Sovrascrive il DAC (Discretionary Access Control), ovvero bypassa i controlli dei permessi di lettura/scrittura/esecuzione. Questa capability garantisce a un eseguibile la possibilità di accedere e modificare qualsiasi file sul filesystem. CAP_NET_BIND_SERVICE: Consente di effettuare il bind su numeri di porta inferiori alla 1024. CAP_KILL: Consente di bypassare i controlli sui permessi per l\u0026rsquo;invio di segnali ai processi come SIGHUP e SIGKILL. CAP_SYS_NICE: Consente di modificare il valore di niceness e la priorità di scheduling dei processi, tra le altre cose. CAP_SYS_RESOURCE: Consente di ignorare vari limiti sulle risorse di sistema, come le quote disco, i limiti di tempo della CPU, ecc. La funzionalità delle capabilities è stata introdotta nel kernel 2.2 nel 1999, ma inizialmente riguardava solo i processi. Nel 2008 sono state introdotte le capabilities anche per i file. Al momento della scrittura, ci sono 40 capabilities definite e implementate; potete ottenere l\u0026rsquo;elenco completo con il comando\n$ systemd-analyze capability oppure nella pagina di manuale capabilities(7).\n🔧 Come usarle? Parlando di userspace, esistono due diversi pacchetti per la gestione delle capabilities: libcap e libcap-ng. Quest\u0026rsquo;ultimo è progettato per essere più semplice del primo, quindi ci concentreremo su quello.\nInstalliamo il pacchetto che useremo per i nostri esperimenti:\n$ sudo zypper install libpcap-ng-utils $ rpm -ql libcap-ng-utils /usr/bin/captest /usr/bin/filecap /usr/bin/netcap /usr/bin/pscap /usr/share/licenses/libcap-ng-utils /usr/share/licenses/libcap-ng-utils/COPYING /usr/share/man/man8/captest.8.gz /usr/share/man/man8/filecap.8.gz /usr/share/man/man8/netcap.8.gz /usr/share/man/man8/pscap.8.gz Il pacchetto fornisce diversi strumenti utili:\ncaptest: Verifica le capabilities del processo corrente filecap: Visualizza o modifica le capabilities dei file netcap: Mostra le capabilities di rete dei programmi esposti in rete pscap: Elenca le capabilities dei processi in esecuzione Utilizzo di filecap: filecap è utilizzato per visualizzare o modificare le capabilities dei file. Ecco come usarlo:\n💻 Un esempio rapido È facile avviare un semplice server HTTP in Python:\n/usr/bin/python3 -m http.server Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ... ^C Keyboard interrupt received, exiting. di default si avvia sulla porta 8000, perché gli utenti non privilegiati non possono fare il bind di porte inferiori:\n$ /usr/bin/python3 -m http.server 80 Traceback (most recent call last): [...] PermissionError: [Errno 13] Permission denied Possiamo concedere al binario Python la capability di effettuare il bind su porte inferiori utilizzando:\n$ sudo filecap /usr/bin/python3 net_bind_service $ /usr/bin/python3 -m http.server 80 Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ... ^C Keyboard interrupt received, exiting. per ripristinare lo stato precedente, possiamo usare la parola chiave none:\n$ sudo filecap /usr/bin/python3 none $ /usr/bin/python3 -m http.server 80 [...] PermissionError: [Errno 13] Permission denied ⚙️ Utilizzo di systemd Systemd, il sistema di init utilizzato in molte moderne distribuzioni Linux, offre un supporto robusto per la gestione delle capabilities. Questa integrazione consente un controllo granulare sui privilegi dei servizi senza doverli eseguire come root.\nDirettive relative alle capabilities: I file unit di systemd supportano diverse direttive per la gestione delle capabilities:\nCapabilityBoundingSet: Limita le capabilities che un servizio può avere. AmbientCapabilities: Concede capabilities aggiuntive a un servizio. SecureBits: Imposta i flag dei bit di sicurezza (secure bits) per limitare ulteriormente l\u0026rsquo;uso delle capabilities. Ecco un esempio di un file unit di servizio di systemd che concede il permesso di effettuare il bind su porte inferiori:\n[Service] User=bob AmbientCapabilities=CAP_NET_BIND_SERVICE 🔍 Uno sguardo all\u0026rsquo;interno Nel kernel Linux, concettualmente le capabilities sono gestite in insiemi (sets), rappresentati come maschere di bit (bit masks). Per tutti i processi in esecuzione, le informazioni sulle capabilities sono mantenute per thread; per i binari nel file system, sono memorizzate negli attributi estesi (extended attributes). Esistono cinque insiemi di capabilities: Permitted, Inheritable, Effective, Bounding e Ambient. Di questi, tuttavia, solo i primi tre possono essere assegnati ai file eseguibili. L\u0026rsquo;insieme Permitted include le capabilities assegnate a un determinato eseguibile; l\u0026rsquo;insieme Effective è un sottoinsieme di quello Permitted e include le capabilities effettivamente utilizzate. Infine, l\u0026rsquo;insieme Inheritable include le capabilities che possono essere ereditate dai processi figli. Per una spiegazione dettagliata del flusso delle capabilities, consultate questo post di blog di Henryk Plötz o quest\u0026rsquo;altro di Adrian Mouat.\nPer i processi in esecuzione, potete facilmente ottenere la maschera di bit guardando in /proc/$PID/status:\n$ grep Cap \u0026#34;/proc/$(pidof chronyd)/status\u0026#34; CapInh:\t0000000000000000 CapPrm:\t0000000002000400 CapEff:\t0000000002000400 CapBnd:\t000001c08380fddf CapAmb:\t0000000000000000 Ed è più semplice da leggere una volta decodificata:\n$ pscap -p $(pidof chronyd) ppid pid uid command capabilities 1 1803 chrony chronyd net_bind_service, sys_time + oppure con l\u0026rsquo;aiuto di capsh (dal pacchetto libcap-progs):\n$ capsh --decode=000001c08380fddf 0x000001c08380fddf=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_setfcap,cap_perfmon,cap_bpf,cap_checkpoint_restore 🎯 Perché dovrebbe interessarmi? Le capabilities offrono un modo per ridurre la superficie di attacco di un sistema concedendo a ciascun servizio solo il livello minimo di privilegi di cui ha bisogno, evitando così la necessità di eseguire i servizi come utente root.\nNell\u0026rsquo;era dei microservizi, dei container e di Kubernetes, le capabilities svolgono un ruolo importante per una serie di motivi:\nControllo di sicurezza granulare: Le capabilities consentono un approccio più granulare alla concessione dei privilegi ai processi, rispetto al tradizionale accesso root \u0026ldquo;tutto o niente\u0026rdquo;. Ciò consente ai container di essere eseguiti solo con i privilegi specifici di cui hanno bisogno, migliorando la sicurezza complessiva del sistema.\nPrincipio del minimo privilegio: Assegnando solo le capabilities necessarie ai container, gli amministratori possono applicare il principio del minimo privilegio. Ciò riduce la potenziale superficie di attacco e limita i danni che potrebbero essere causati se un container venisse compromesso.\nCompatibilità con container non-root: Molte organizzazioni preferiscono eseguire i container come utenti non-root per motivi di sicurezza. Le capabilities consentono a questi container non-root di eseguire specifiche operazioni privilegiate senza richiedere l\u0026rsquo;accesso root completo.\nKubernetes Pod Security Policies / Security Context: In Kubernetes, è possibile sfruttare le capabilities di Linux per definire una serie di condizioni che un pod deve soddisfare per essere accettato nel sistema. Ciò consente agli amministratori del cluster di applicare le best practice di sicurezza sull\u0026rsquo;intero cluster. Utilizzando il SecurityContext nel manifest di Kubernetes, è possibile impostare le capabilities nei container.\nIsolamento dei container: Le capabilities aiutano a mantenere un forte isolamento tra i container e il sistema host, così come tra i diversi container, limitando ciò che ciascun container può fare.\nRequisiti di conformità (compliance): Molti standard di sicurezza e framework di conformità richiedono il principio del minimo privilegio. L\u0026rsquo;uso delle capabilities aiuta le organizzazioni a soddisfare questi requisiti consentendo comunque ai container di funzionare come necessario.\nFlessibilità nel design dei container: I progettisti e sviluppatori possono progettare container che richiedono specifiche operazioni privilegiate senza la necessità di eseguire l\u0026rsquo;intero container come root, portando a progetti software più sicuri e flessibili.\n🔗 Ulteriori letture Documentazione sulla sicurezza di Docker relativa alle Linux capabilities Documentazione di systemd sull\u0026rsquo;ambiente di esecuzione (execution environment) Pagina del progetto libcap-ng 🏁 Conclusioni Le capabilities di Linux rappresentano un approccio potente e flessibile alla sicurezza. Scomponendo i tradizionali privilegi di root \u0026ldquo;tutto o niente\u0026rdquo; in permessi più granulari, le capabilities consentono ad amministratori di sistema e sviluppatori di implementare efficacemente il principio del minimo privilegio.\n","permalink":"https://ilmanzo.github.io/it/post/playing_with_linux_capabilities/","summary":"\u003ch2 id=\"-introduzione\"\u003e🔐 Introduzione\u003c/h2\u003e\n\u003cp\u003eCome sysadmin esperti, avrete probabilmente familiarità con il tradizionale approccio \u0026ldquo;tutto o niente\u0026rdquo;: se una shell o un processo è in esecuzione con \u003ccode\u003eUID==0\u003c/code\u003e, può fare quasi tutto sul sistema; al contrario, un processo utente comune è limitato in vari modi: tipicamente non può aprire socket RAW, non può fare il bind di porte \u0026ldquo;privilegiate\u0026rdquo; inferiori alla 1024, non può cambiare la proprietà di un file (chown) e così via.\u003c/p\u003e","title":"Giocare con le capabilities del kernel Linux"},{"content":"🌡️ Introduzione Per chi gestisce un datacenter, o anche un semplice server homelab, l\u0026rsquo;arrivo del caldo estivo si traduce in un aumento dell\u0026rsquo;uso dell\u0026rsquo;aria condizionata. In questo post mi sono chiesto come un ingegnere Linux possa misurare quanta energia sta consumando il sistema, in modo da poter iniziare a ragionare sull\u0026rsquo;ottimizzazione dei carichi di lavoro (workload) per ottenere pattern di consumo energetico migliori.\n🔋 Consumo energetico in idle Come punto di partenza, misuriamo quanta energia consuma il mio PC quando è in idle, cioè non fa assolutamente nulla; o meglio: nulla di utile ai fini del calcolo o di un servizio, ma esegue solo le solite attività predefinite del sistema operativo.\nCi sono molti modi per farlo; il più affidabile probabilmente consiste nell\u0026rsquo;utilizzare un misuratore di potenza esterno (power meter) come questo.\nimmagine di pubblico dominio da Flickr; crediti a Emilian Robert Vicol\nEssendo un ingegnere del software, non mi va molto di armeggiare con i cavi; e in molte occasioni non è pratico scollegare i server in un datacenter remoto solo per fare una rapida misurazione energetica. Come soluzione puramente software, alcune misurazioni approssimative si possono ottenere con l\u0026rsquo;aiuto dell\u0026rsquo;utilità powerstat, che utilizza diversi metodi (come le statistiche della batteria o l\u0026rsquo;interfaccia Intel RAPL, che sta per Running Average Power Limit) per stimare il consumo energetico di un computer in funzione.\n# powerstat -zR [...] -------- ----- ----- ----- ----- ----- ---- ------ ------ ---- ---- ---- ------ Average 0.1 0.0 0.2 99.4 0.2 1.1 916.3 479.6 0.2 0.0 0.4 29.98 GeoMean 0.1 0.0 0.2 99.4 0.2 1.1 882.5 463.9 0.0 0.0 0.0 29.89 StdDev 0.1 0.0 0.1 0.3 0.2 0.3 408.7 169.7 0.7 0.0 2.1 2.64 -------- ----- ----- ----- ----- ----- ---- ------ ------ ---- ---- ---- ------ Minimum 0.1 0.0 0.1 96.9 0.1 1.0 730.0 377.0 0.0 0.0 0.0 28.74 Maximum 0.8 0.0 0.6 99.6 1.6 2.0 3987.0 1589.0 5.0 0.0 16.0 44.31 -------- ----- ----- ----- ----- ----- ---- ------ ------ ---- ---- ---- ------ Summary: CPU: 29.98 Watts on average with standard deviation 2.64 Note: power read from RAPL domains: uncore, pkg-0, core, psys. These readings do not cover all the hardware in this device. ⚡ Mettiamolo alla prova Quindi il mio computer, un ThinkPad P15 Gen 2i, consuma circa ~30 Watt (o ~30 Joule/sec) per non fare nulla, il che, se volete il mio parere, considero piuttosto scadente. Facciamo qualche esperimento con carichi di lavoro differenti, come quello in cui ogni computer dovrebbe eccellere: il calcolo dei numeri primi!\nL\u0026rsquo;idea dell\u0026rsquo;esperimento è vedere quanta energia costa ottenere il primo milione di numeri primi; in base a quanto riportato sulla wiki dei numeri primi, il milionesimo numero primo dovrebbe essere 15.485.863.\nHo iniziato con un programma Python piuttosto semplice e volutamente non ottimizzato:\nfrom math import sqrt def isPrime(n): for j in range(3,int(sqrt(n)+1),2): if n % j == 0: return False return True n,i = 2,3 while n \u0026lt; 1_000_000: i += 2 if isPrime(i): n += 1 print(\u0026#34;Prime number =\u0026#34;, i) E l\u0026rsquo;ho eseguito con la misurazione di powerstat in background:\n# (powerstat -zR 1 60 \u0026amp;) ; /usr/bin/time python3 /tmp/prime.py -------- ----- ----- ----- ----- ----- ---- ------ ------ ---- ---- ---- ------ Average 6.3 0.0 0.2 93.4 0.1 2.0 1003.4 1168.7 0.1 0.0 0.5 56.65 GeoMean 6.3 0.0 0.1 93.4 0.0 2.0 872.7 964.3 0.0 0.0 0.0 56.26 StdDev 0.1 0.0 0.1 0.1 0.0 0.1 750.7 1184.1 0.4 0.1 1.7 7.55 -------- ----- ----- ----- ----- ----- ---- ------ ------ ---- ---- ---- ------ Minimum 6.3 0.0 0.1 92.7 0.0 2.0 561.0 697.0 0.0 0.0 0.0 53.39 Maximum 7.1 0.0 0.4 93.6 0.1 3.0 4135.0 6576.0 2.0 1.0 10.0 90.93 -------- ----- ----- ----- ----- ----- ---- ------ ------ ---- ---- ---- ------ Summary: CPU: 56.65 Watts on average with standard deviation 7.55 Note: power read from RAPL domains: uncore, pkg-0, core, psys. These readings do not cover all the hardware in this device. Prime = 15485863 61.68user 0.00system 1:01.71elapsed 99%CPU (0avgtext+0avgdata 8064maxresident)k 0inputs+0outputs (0major+879minor)pagefaults 0swaps Notate come i numeri variano nel tempo! Quindi, durante i calcoli, il mio PC quasi raddoppia il consumo di energia. Per andare sul sicuro, diciamo che circa ~27W è l\u0026rsquo;energia extra richiesta per eseguire il mio modesto calcolatore di numeri primi in Python.\nPoiché il programma viene eseguito per circa ~61,5 secondi, l\u0026rsquo;esecuzione richiede circa ~3490 Joule di energia o 0,0009694444 kWh; naturalmente, questo si riferisce solo alla CPU e non include scheda grafica, sottosistema del disco, display, rete e così via.\n📏 Una misurazione alternativa Un altro modo per leggere le metriche RAPL è utilizzare la potente utilità perf. Questo strumento può leggere molte sonde esposte dal kernel, quindi forse in futuro dovremmo dedicargli un intero post.\n# perf list --details power* List of pre-defined events (to be used in -e or -M): power/energy-cores/ [Kernel PMU event] power/energy-pkg/ [Kernel PMU event] power/energy-psys/ [Kernel PMU event] [output omitted] Il contatore di performance power/energy-cores/ fa parte dell\u0026rsquo;interfaccia Intel RAPL, basato su un registro MSR della CPU chiamato MSR_PP0_ENERGY_STATUS. Gli altri due sono misurazioni di potenza relative al pacchetto (core e componenti uncore) e all\u0026rsquo;intero System on Chip; quest\u0026rsquo;ultimo è disponibile a partire dall\u0026rsquo;architettura Skylake.\nEseguiamo il nostro programma Python chiedendo a perf di raccogliere le metriche durante l\u0026rsquo;esecuzione:\n# perf stat -ae power/energy-cores/,power/energy-pkg/,power/energy-psys/ python3 /tmp/prime.py Prime = 15485863 Performance counter stats for \u0026#39;system wide\u0026#39;: 695.00 Joules power/energy-cores/ 930.12 Joules power/energy-pkg/ 1855.48 Joules power/energy-psys/ 61.362063830 seconds time elapsed 🚀 Possiamo fare di meglio Per un confronto di prestazioni, verifichiamo lo stesso algoritmo scritto in Rust:\nfn is_prime(n: u64) -\u0026gt; bool { let sqrt_n = (n as f64).sqrt() as u64; for j in (3..=sqrt_n).step_by(2) { if n % j == 0 { return false; } } true } fn main() { let mut n = 2; let mut i = 3; while n \u0026lt; 1_000_000 { i += 2; if is_prime(i) { n += 1; } } println!(\u0026#34;Prime = {}\u0026#34;, i); } Nota: abbiamo semplicemente tradotto il codice Python, senza prestare alcuna attenzione all\u0026rsquo;ottimizzazione.\n$ cargo build --release Compiling prime1m v0.1.0 (/home/andrea/projects/prove/prime1m) Finished `release` profile [optimized] target(s) in 0.12s # perf stat -ae power/energy-cores/,power/energy-pkg/,power/energy-psys/ ./target/release/prime1m Prime = 15485863 Performance counter stats for \u0026#39;system wide\u0026#39;: 24.46 Joules power/energy-cores/ 36.17 Joules power/energy-pkg/ 82.12 Joules power/energy-psys/ 2.987509303 seconds time elapsed Come previsto, a parità di output, il programma compilato è circa ~20 volte più veloce e consuma meno risorse ed energia. Sembra interessante!\n📚 Approfondimenti RAPL (Running Average Power Limit) è una funzionalità hardware introdotta da Intel per facilitare la gestione energetica; l\u0026rsquo;interfaccia RAPL è trattata nella Sezione 14.9 del Manuale Intel Volume 3. Se volete saperne di più su questo framework, vi consiglio anche questo articolo di ricerca.\nLo strumento perf può fare molto di più che leggere le metriche di potenza dal kernel. Se volete vedere alcuni esempi, visitate l\u0026rsquo;onnipresente sito web di Brendan Gregg.\nBuon divertimento e happy green computing!\n","permalink":"https://ilmanzo.github.io/it/post/measure_your_power_consumption/","summary":"\u003ch2 id=\"-introduzione\"\u003e🌡️ Introduzione\u003c/h2\u003e\n\u003cp\u003ePer chi gestisce un datacenter, o anche un semplice server homelab, l\u0026rsquo;arrivo del caldo estivo si traduce in un aumento dell\u0026rsquo;uso dell\u0026rsquo;aria condizionata. In questo post mi sono chiesto come un ingegnere Linux possa misurare quanta energia sta consumando il sistema, in modo da poter iniziare a ragionare sull\u0026rsquo;ottimizzazione dei carichi di lavoro (workload) per ottenere pattern di consumo energetico migliori.\u003c/p\u003e\n\u003ch2 id=\"-consumo-energetico-in-idle\"\u003e🔋 Consumo energetico in idle\u003c/h2\u003e\n\u003cp\u003eCome punto di partenza, misuriamo quanta energia consuma il mio PC quando è in idle, cioè non fa assolutamente nulla; o meglio: nulla di utile ai fini del calcolo o di un servizio, ma esegue solo le solite attività predefinite del sistema operativo.\u003c/p\u003e","title":"Misurare il consumo energetico dei propri programmi"},{"content":"🤓 Introduzione Il testing manuale delle applicazioni GUI può diventare noioso e soggetto a errori con l\u0026rsquo;aumento delle funzionalità e della complessità. Il testing headless offre una soluzione automatizzando le interazioni dell\u0026rsquo;interfaccia utente (UI) senza la necessità di un display fisico. Questo approccio consente un\u0026rsquo;esecuzione più rapida dei test, una migliore ripetibilità e una perfetta integrazione con le pipeline di continuous integration e continuous delivery (CI/CD). In questo post esploreremo come sfruttare pytest-qt, un potente framework per il testing headless di applicazioni PySide/PyQt.\nCome al solito, tutto il codice è disponibile nel mio repository GitHub.\n🧮 Esempio Come punto di partenza, supponiamo di avere un\u0026rsquo;app piuttosto semplice, composta solo da due pulsanti e un\u0026rsquo;etichetta (label).\nSì, il giorno dell\u0026rsquo;asciugamano (Towel Day) si avvicina.\n🤬 Il problema L\u0026rsquo;applicazione è piuttosto semplice: un\u0026rsquo;etichetta numerica che dovrebbe essere rossa quando mostra numeri dispari, verde per i numeri pari. L\u0026rsquo;utente può solo incrementare o decrementare il contatore con i pulsanti corrispondenti.\nQuando premiamo il pulsante +1, il numero aumenta correttamente:\nUn utente ci mostra gentilmente il problema: quando premiamo il pulsante -1, succede qualcosa di strano!\nProbabilmente per questo semplice problema potremmo limitarci a ispezionare il codice e trovare l\u0026rsquo;errore in pochissimo tempo, ma dato che siamo dei tester, vogliamo aggiungere alcuni test alla nostra applicazione interattiva.\n🧑‍🔬 Il primo test Per convalidare l\u0026rsquo;errore, utilizzeremo il plugin pytest-qt per il framework pytest. Creare un test per riprodurre il problema non solo ci aiuta a individuare l\u0026rsquo;errore specifico, ma contribuisce a creare una suite di test in crescita che preverrà anche future regressioni.\nIniziamo con un test semplice: l\u0026rsquo;applicazione deve essere in grado di avviarsi e il valore iniziale del contatore deve essere 42. Con pytest si tratta di una sola funzione:\nfrom mainwindow import MainWindow def test_application_start(qtbot): widget = MainWindow() qtbot.addWidget(widget) assert widget.number == 42 qtbot è una Fixture decisamente comoda fornita dal plugin pytest-qt; per \u0026ldquo;eseguire\u0026rdquo; la nostra app dobbiamo semplicemente aggiungere il nostro widget all\u0026rsquo;oggetto qtbot.\nesecuzione del test:\n$ pytest =================== test session starts ====================== platform linux -- Python 3.11.9, pytest-8.2.1, pluggy-1.5.0 PySide6 6.7.0 -- Qt runtime 6.7.0 -- Qt compiled 6.7.0 rootdir: /home/andrea/projects/pyside-playground/testable_app configfile: pytest.ini plugins: qt-4.4.0 collected 1 item test_mainwindow.py . [100%] =================== 1 passed in 0.09s ======================== Successo! Come potete vedere, la Fixture pytest-qt gestisce per noi tutti i dettagli più noiosi relativi all\u0026rsquo;istanziazione di una QApplication e all\u0026rsquo;esecuzione di un event loop in ascolto di signal/slot. La GUI non viene nemmeno renderizzata sullo schermo, rendendo questo approccio adatto all\u0026rsquo;esecuzione anche in un ambiente automatizzato come una CI.\n✅ Il secondo test Aggiungiamo un secondo test: diciamo all\u0026rsquo;applicazione di \u0026ldquo;premere\u0026rdquo; il pulsante +1 e ci aspettiamo sia il numero corretto che l\u0026rsquo;attributo colore dell\u0026rsquo;etichetta:\nimport pytest from pytestqt.qt_compat import qt_api from mainwindow import MainWindow @pytest.fixture def widget(qtbot): widget = MainWindow() qtbot.addWidget(widget) return widget def test_application_start(qtbot, widget): assert widget.number == 42 def test_inc_button(qtbot, widget): # click in the + button and make sure it updates the numeric label qtbot.mouseClick(widget.ui.btnInc, qt_api.QtCore.Qt.MouseButton.LeftButton) assert widget.ui.lbl_number.text()==\u0026#34;43\u0026#34; assert \u0026#34;background-color: red\u0026#34; in widget.ui.lbl_number.styleSheet() Abbiamo anche rifattorizzato la parte comune, fornendo una fixture di setup del widget che gestisce lo stato iniziale per ciascun test. Anche la seconda esecuzione è verde:\n$ pytest =================== test session starts ====================== platform linux -- Python 3.11.9, pytest-8.2.1, pluggy-1.5.0 PySide6 6.7.0 -- Qt runtime 6.7.0 -- Qt compiled 6.7.0 rootdir: /home/andrea/projects/pyside-playground/testable_app configfile: pytest.ini plugins: qt-4.4.0 collected 2 items test_mainwindow.py .. [100%] =================== 2 passed in 0.09s ======================== 🐞 Un test fallito Ci manca solo il terzo test, che dovrebbe innescare il problema:\nimport pytest from pytestqt.qt_compat import qt_api from mainwindow import MainWindow @pytest.fixture def widget(qtbot): widget = MainWindow() qtbot.addWidget(widget) return widget def test_application_start(qtbot,widget): assert widget.number == 42 def test_inc_button(qtbot,widget): # click in the + button and make sure it updates the numeric label qtbot.mouseClick(widget.ui.btnInc, qt_api.QtCore.Qt.MouseButton.LeftButton) assert widget.ui.lbl_number.text()==\u0026#34;43\u0026#34; assert \u0026#34;background-color: red\u0026#34; in widget.ui.lbl_number.styleSheet() def test_dec_button(qtbot,widget): # click in the - button and make sure it updates the numeric label qtbot.mouseClick(widget.ui.btnDec, qt_api.QtCore.Qt.MouseButton.LeftButton) assert widget.ui.lbl_number.text()==\u0026#34;41\u0026#34; assert \u0026#34;background-color: red\u0026#34; in widget.ui.lbl_number.styleSheet() AHA! (portate pazienza, per un ingegnere QA un test che fallisce è pura felicità)\n$ pytest =========================== test session starts ========================== platform linux -- Python 3.11.9, pytest-8.2.1, pluggy-1.5.0 PySide6 6.7.0 -- Qt runtime 6.7.0 -- Qt compiled 6.7.0 rootdir: /home/andrea/projects/pyside-playground/testable_app configfile: pytest.ini plugins: qt-4.4.0 collected 3 items test_mainwindow.py ..F [100%] =============================== FAILURES ================================= ___________________________ test_dec_button ______________________________ qtbot = \u0026lt;pytestqt.qtbot.QtBot object at 0x7fc7912d1210\u0026gt; widget = \u0026lt;mainwindow.MainWindow(0x5615c0cb2ae0, name=\u0026#34;MainWindow\u0026#34;) at 0x7fc7912d1580\u0026gt; def test_dec_button(qtbot,widget): # click in the - button and make sure it updates the numeric label qtbot.mouseClick(widget.ui.btnDec, qt_api.QtCore.Qt.MouseButton.LeftButton) \u0026gt; assert widget.ui.lbl_number.text()==\u0026#34;41\u0026#34; E AssertionError: assert \u0026#39;-1\u0026#39; == \u0026#39;41\u0026#39; E E - 41 E + -1 test_mainwindow.py:25: AssertionError ========================= short test summary info ========================= FAILED test_mainwindow.py::test_dec_button - AssertionError: assert \u0026#39;-1\u0026#39; == \u0026#39;41\u0026#39; ========================= 1 failed, 2 passed in 0.11s ===================== Premendo -1 ci aspettiamo che l\u0026rsquo;etichetta mostri \u0026ldquo;41\u0026rdquo;, ma mostra -1. Nel test possiamo non solo verificare l\u0026rsquo;aspetto dell\u0026rsquo;etichetta, ma anche ispezionare lo stato interno dell\u0026rsquo;applicazione e chiarire se il comportamento errato sia causato da un errore di battitura o da qualche bug logico.\n✍️ Conclusioni Abbiamo trovato l\u0026rsquo;errore e ora risolverlo è un gioco da ragazzi. Quando l\u0026rsquo;applicazione è davvero complessa, è fondamentale disporre di una suite di test che convalidi il comportamento dell\u0026rsquo;interfaccia, perché all\u0026rsquo;aumentare della complessità il testing manuale diventa ripetitivo e molto soggetto a errori. Ad esempio, possiamo facilmente verificare che il numero non superi i limiti:\ndef test_inc_button_10_times(qtbot,widget): # click 10 times the + button and make sure it updates the numeric label for i in range(10): qtbot.mouseClick(widget.ui.btnInc, qt_api.QtCore.Qt.MouseButton.LeftButton) assert widget.ui.lbl_number.text()==\u0026#34;52\u0026#34; assert \u0026#34;background-color: green\u0026#34; in widget.ui.lbl_number.styleSheet() def test_inc_button_100_times(qtbot,widget): # click 100 times the + button and make sure it updates the numeric label for i in range(100): qtbot.mouseClick(widget.ui.btnInc, qt_api.QtCore.Qt.MouseButton.LeftButton) assert widget.ui.lbl_number.text()==\u0026#34;100\u0026#34; Questi test vengono eseguiti in un decimo di secondo e possono salvarci da molti futuri bug.\nCome abbiamo visto, pytest-qt offre un modo potente ed efficiente per automatizzare i test per le applicazioni GUI PySide/PyQt. Con modifiche minime al codice, possiamo ottenere una robusta copertura di test e garantire un comportamento coerente dell\u0026rsquo;applicazione. Sentitevi liberi di esplorare ulteriormente l\u0026rsquo;esempio di codice fornito e di sfruttare pytest-qt nei vostri progetti! Per un approfondimento, prendete in considerazione l\u0026rsquo;esplorazione della documentazione ufficiale di pytest-qt e pytest. Sebbene il testing headless eccella nella convalida funzionale, è importante ricordare che potrebbe non coprire tutti gli aspetti del testing della UI, come la verifica del layout visivo.\nCome ultimo suggerimento, ricordatevi di configurare il vostro file pytest.ini con la versione specifica del framework:\n$ cat pytest.ini [pytest] qt_api=pyside6 Buon divertimento!\n","permalink":"https://ilmanzo.github.io/it/post/testing_pyside_gui_applications/","summary":"\u003ch2 id=\"-introduzione\"\u003e🤓 Introduzione\u003c/h2\u003e\n\u003cp\u003eIl testing manuale delle applicazioni GUI può diventare noioso e soggetto a errori con l\u0026rsquo;aumento delle funzionalità e della complessità. Il testing headless offre una soluzione automatizzando le interazioni dell\u0026rsquo;interfaccia utente (UI) senza la necessità di un display fisico. Questo approccio consente un\u0026rsquo;esecuzione più rapida dei test, una migliore ripetibilità e una perfetta integrazione con le pipeline di continuous integration e continuous delivery (CI/CD). In questo post esploreremo come sfruttare pytest-qt, un potente framework per il testing headless di applicazioni \u003ca href=\"https://wiki.qt.io/Qt_for_Python\"\u003ePySide/PyQt\u003c/a\u003e.\u003c/p\u003e","title":"Testing Headless di Applicazioni GUI PySide/PyQt con pytest-qt"},{"content":"Introduzione Bubbletea è un framework con una filosofia basata su The Elm Architecture: Per farla semplice, si divide in tre parti:\nModel: lo stato della tua applicazione View: un modo per trasformare il tuo stato in qualcosa da mostrare a schermo Update: un modo per aggiornare lo stato in base ai messaggi Il runtime del framework gestisce tutto il resto: dall\u0026rsquo;orchestrazione dei messaggi ai dettagli di rendering a basso livello.\nEsempio Ipotizziamo di voler creare la classica lista di cose da fare (to-do list):\nil modello sarà una struct contenente una lista di task e un flag per contrassegnarli come completati (\u0026ldquo;Done\u0026rdquo;) type model struct { tasks []string // items on the to-do list done []bool // which to-do items are done }\nla vista sarà una funzione che accetta il modello e restituisce una rappresentazione sotto forma di stringa della lista dei task func (m model) View() string { var s strings.Builder // Iterate over our choices for i, tasks := range m.tasks { // Is this task done? if m.done[i] { s.WriteString(\u0026#34;[x] \u0026#34;) } else { s.WriteString(\u0026#34;[ ] \u0026#34;) } s.WriteString(tasks[i]+\u0026#34;\\n\u0026#34;) } return s.String() }\nl\u0026rsquo;aggiornamento sarà una funzione che riceve un messaggio proveniente dal framework e reagisce ad esso, ad esempio la pressione di un tasto, un clic del mouse o il ridimensionamento della finestra. Consulta la documentazione per i dettagli. func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { // here you need to process events from the framework // and return an updated model plus optionally a new Command to execute } }\nCome ultimo passaggio, occorre solo passare il modello al framework: func main() { p := tea.NewProgram(model{}) if _, err := p.Run(); err != nil { fmt.Printf(\u0026#34;There\u0026#39;s been an error: %v\u0026#34;, err) os.Exit(1) } }\nQualcosa di divertente Invece della noiosa to-do list, come altro esempio ho implementato la classica \u0026ldquo;palla che rimbalza\u0026rdquo; (bouncing ball) con pareti ridimensionabili; si può vedere come il programma reagisca al ridimensionamento della finestra agendo di conseguenza.\nVideo a schermo intero qui\nIl codice sorgente è piuttosto semplice:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package main import ( \u0026#34;fmt\u0026#34; \u0026#34;os\u0026#34; \u0026#34;strings\u0026#34; \u0026#34;time\u0026#34; tea \u0026#34;github.com/charmbracelet/bubbletea\u0026#34; ) type model struct { width int height int x int y int dx int dy int } type tickMsg time.Time func (m model) Init() tea.Cmd { return tickCmd() } // Custom message tied to a timer func tickCmd() tea.Cmd { return tea.Tick(time.Millisecond*20, func(t time.Time) tea.Msg { return tickMsg(t) }) } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.height = msg.Height m.width = msg.Width case tickMsg: x1 := m.x + m.dx y1 := m.y + m.dy if x1 \u0026lt; 0 || x1 \u0026gt; m.width { m.dx = -m.dx } if y1 \u0026lt; 0 || y1 \u0026gt; m.height { m.dy = -m.dy } m.x += m.dx m.y += m.dy return m, tickCmd() case tea.KeyMsg: switch msg.String() { case \u0026#34;ctrl+c\u0026#34;, \u0026#34;q\u0026#34;: return m, tea.Quit } } // Return the updated model to the Bubble Tea runtime for processing. return m, nil } func (m model) View() string { var s strings.Builder for i := 0; i \u0026lt; m.y; i++ { s.WriteString(\u0026#34;\\n\u0026#34;) } s.WriteString(strings.Repeat(\u0026#34; \u0026#34;, m.x)) s.WriteString(\u0026#34;o\u0026#34;) return s.String() } func main() { p := tea.NewProgram(model{x: 1, y: 1, dx: 1, dy: 1}, tea.WithAltScreen()) if _, err := p.Run(); err != nil { fmt.Printf(\u0026#34;There\u0026#39;s been an error: %v\u0026#34;, err) os.Exit(1) } } L\u0026rsquo;unica grande differenza qui è un tipo di messaggio personalizzato, utilizzato per inviare eventi periodici all\u0026rsquo;applicazione in modo che si aggiorni da sola senza l\u0026rsquo;interazione dell\u0026rsquo;utente. Sembra proprio che si possano scrivere anche dei giochi \u0026#x1f609; !\nNaturalmente puoi trovare moltissimi esempi nella repository della libreria.\nConclusioni Insieme a Bubbletea, consiglio anche alcune librerie di \u0026ldquo;supporto\u0026rdquo; ideali, tutte disponibili su https://github.com/charmbracelet/:\nBubbles per componenti già pronti Lipgloss per colorazione, layout e stile, più o meno un \u0026ldquo;CSS per il terminale\u0026rdquo; Buon divertimento!\n","permalink":"https://ilmanzo.github.io/it/post/first_steps_with_golang_tui/","summary":"\u003ch2 id=\"introduzione\"\u003eIntroduzione\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/charmbracelet/bubbletea\"\u003eBubbletea\u003c/a\u003e è un framework con una filosofia basata su \u003ca href=\"https://guide.elm-lang.org/architecture/\"\u003eThe Elm Architecture\u003c/a\u003e:\nPer farla semplice, si divide in tre parti:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cem\u003eModel\u003c/em\u003e: lo stato della tua applicazione\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eView\u003c/em\u003e: un modo per trasformare il tuo stato in qualcosa da mostrare a schermo\u003c/li\u003e\n\u003cli\u003e\u003cem\u003eUpdate\u003c/em\u003e: un modo per aggiornare lo stato in base ai messaggi\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eIl runtime del framework gestisce tutto il resto: dall\u0026rsquo;orchestrazione dei messaggi ai dettagli di rendering a basso livello.\u003c/p\u003e\n\u003ch2 id=\"esempio\"\u003eEsempio\u003c/h2\u003e\n\u003cp\u003eIpotizziamo di voler creare la classica lista di cose da fare (to-do list):\u003c/p\u003e","title":"Un approccio diverso alle interfacce utente da terminale"},{"content":"🙀 Qualcosa si rompe Se usate Linux ed eseguite alcune applicazioni, potreste aver visto a volte un messaggio di errore simile a questo:\n[1] 24975 segmentation fault (core dumped) cosa significa e come può essere utile? Scopriamolo insieme.\n🧪 Preparazione del laboratorio Per realizzare un caso di studio adeguato, abbiamo bisogno di un programma che vada in crash. Naturalmente al giorno d\u0026rsquo;oggi sono molto rari \u0026#x1f601; quindi ne creeremo uno noi stessi, mettendo in mostra le nostre peggiori abilità e pratiche di programmazione in C.\nAttenzione: questo programma è scritto male appositamente per mostrare come funziona un segmentation fault, non è inteso per nessun altro scopo\u0026hellip; Non fatelo in un contesto reale.\n// badprogram.c #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;string.h\u0026gt; void makeitcrash() { char buffer[10000]; memset(buffer, 0, sizeof(buffer)); for(int i=1; i\u0026lt;100000; i*=2) buffer[i]=\u0026#39;A\u0026#39;; // typo: a zero in excess } int main() { printf(\u0026#34;Kaboom!\u0026#34;); makeitcrash(); return 0; } ora dobbiamo compilarlo, inserendo anche i simboli di debug nel binario, quindi useremo il flag -g:\n$ gcc -g badprogram.c -o badprogram ./badprogram [1] 7022 segmentation fault (core dumped) ./badprogram Ottimo, è fallito! Ma cosa è successo realmente? Come e dove ha \u0026ldquo;scaricato\u0026rdquo; (dumped) qualcosa? \u0026#x1f4a9;\n🐕 Un po\u0026rsquo; di indagine Ci sono molte risorse là fuori che spiegano questa cosa meglio di me, ma per farla breve, quando un programma tenta di accedere a una regione di memoria non valida (in qualsiasi modo, ad esempio dereferenziando un puntatore NULL), il sistema operativo invia il segnale 11 (SIGSEGV) al processo. Il gestore dei segnali predefinito crea opzionalmente un file di \u0026ldquo;dump\u0026rdquo; che contiene la memoria del processo al momento dell\u0026rsquo;errore e lo termina con un messaggio improvviso.\nPoiché possono occupare molto spazio, di solito i core dump sono compressi di default; possiamo verificare alcune informazioni sul core scaricato con l\u0026rsquo;utilità coredumpctl:\n$ coredumpctl info badprogram PID: 4269 (badprogram) UID: 1000 (andrea) GID: 1000 (andrea) Signal: 11 (SEGV) Timestamp: Fri 2024-04-05 11:13:45 CEST (6s ago) Command Line: ./badprogram Executable: /home/andrea/projects/coredumper/badprogram Unit: user@1000.service User Unit: vte-spawn-68d635ed-58e5-4522-95ee-2deb64da991a.scope Slice: user-1000.slice Owner UID: 1000 (andrea) Boot ID: [blablabla] Machine ID: [blablabla] Hostname: localhost Storage: /var/lib/systemd/coredump/core.badprogram.1000.9d49cca5818645e4baacc1ddddd7a9e8.4269.1712308425000000.zst (present) Size on Disk: 25.3K Message: Process 4269 (badprogram) of user 1000 dumped core. Stack trace of thread 4269: #0 0x0000000000401152 n/a (/home/andrea/projects/prove/coredumper/badprogram + 0x1152) #1 0x0000000000401175 n/a (/home/andrea/projects/prove/coredumper/badprogram + 0x1175) #2 0x00007f112f02a1f0 __libc_start_call_main (libc.so.6 + 0x2a1f0) #3 0x00007f112f02a2b9 __libc_start_main@@GLIBC_2.34 (libc.so.6 + 0x2a2b9) #4 0x0000000000401075 n/a (/home/andrea/projects/prove/coredumper/badprogram + 0x1075) ELF object binary architecture: AMD x86-64 nota a margine: si può sempre ottenere un dump da un programma in esecuzione inviando un segnale SIGABRT al suo ID di processo, come:\n$ kill -ABRT $(pidof firefox-bin)\n⛏️ Più a fondo Decomprimiamo quel file compresso in una posizione comoda ed esaminiamolo un po':\n$ zstd --uncompress /var/lib/systemd/coredump/core.badprogram.1000.9d49cca5818645e4baacc1ddddd7a9e8.4269.1712308425000000.zst -o badprogram.core /var/lib/systemd/coredump/core.badprogram.1000.9d49cca5818645e4baacc1ddddd7a9e8.4269.1712308425000000.zst: 475136 bytes $ ls -l -rwxr-xr-x 1 andrea andrea 21152 apr 5 11:08 badprogram* -rw-r--r-- 1 andrea andrea 240 apr 5 09:30 badprogram.c -rw-r----- 1 andrea andrea 475136 apr 5 11:13 badprogram.core Ora eseguiamo nuovamente il programma difettoso, ma questa volta con l\u0026rsquo;aiuto del Gnu Debugger e passando anche il file coredump:\n$ gdb ./badprogram -c badprogram.core Program terminated with signal SIGSEGV, Segmentation fault. #0 0x0000000000401188 in makeitcrash () at badprogram.c:7 7\tfor(int i=1; i\u0026lt;100000; i*=2) buffer[i]=\u0026#39;A\u0026#39;; (gdb) bt #0 0x0000000000401188 in makeitcrash () at badprogram.c:7 #1 0x00000000004011bd in main () at badprogram.c:12 (gdb) print i $1 = 32768 (gdb) $2 = 32768 (gdb) info locals i = 32768 buffer = \u0026#34;\\000AA\\000A\\000\\000\\000A\\000\\000\\000\\000\\000\\000\\000A\u0026#34;, \u0026#39;\\000\u0026#39; \u0026lt;repeats 15 times\u0026gt;, \u0026#34;A\u0026#34;, \u0026#39;\\000\u0026#39; \u0026lt;repeats 31 times\u0026gt;, \u0026#34;A\u0026#34;, \u0026#39;\\000\u0026#39; \u0026lt;repeats 63 times\u0026gt;, \u0026#34;A\u0026#34;, \u0026#39;\\000\u0026#39; \u0026lt;repeats 127 times\u0026gt;... grazie alle informazioni di debug incluse durante la compilazione, siamo in grado di vedere la sorgente del codice incriminato e il valore della variabile quando si è verificato il problema, offrendoci un punto di partenza per rimediare al nostro errore.\nQuindi, alla fine, qual è la differenza tra avere un file coredump o meno? Perché non possiamo semplicemente ri-eseguire lo stesso programma e lasciarlo andare in crash sotto GDB?\nIl punto è avere tra le mani l'esatto snapshot della memoria del programma nel momento in cui è andato in crash.\nForse non è fattibile eseguirlo ora, oppure non abbiamo lo stesso ambiente o non possiamo ricreare las stesse condizioni sul nostro PC. Ma grazie ai core dump siamo in grado di viaggiare nel tempo fino a quel passato.\nConsiglio extra: esegui gdb con l\u0026rsquo;opzione -tui o digita tui enable al prompt per ottenere una bella interfaccia intuitiva \u0026#x1f604; 🚪 Considerazioni finali Naturalmente non sarà sempre così facile, ma essere in grado di ispezionare la memoria di un processo di un programma terminato è uno strumento di debugging inestimabile. Un\u0026rsquo;osservazione importante è che spesso i pacchetti che installiamo non hanno informazioni di debug incluse, quindi dobbiamo installarle separatamente o compilare il codice sorgente da soli. Anche il kernel stesso può essere analizzato, utilizzando i kernel crash core dump. Su Linux, persino gli errori e i crash sono belli e utili!\nCome approfondimento sul debugging con GDB, consiglio di leggere questo eccellente post sul blog di Brendan Gregg.\n","permalink":"https://ilmanzo.github.io/it/post/debugging_segfault/","summary":"\u003ch2 id=\"-qualcosa-si-rompe\"\u003e🙀 Qualcosa si rompe\u003c/h2\u003e\n\u003cp\u003eSe usate Linux ed eseguite alcune applicazioni, potreste aver visto a volte un messaggio di errore simile a questo:\u003c/p\u003e\n\u003ch2 id=\"1----24975-segmentation-fault-core-dumped\"\u003e\u003ccode\u003e[1]    24975 segmentation fault (core dumped)\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003ecosa significa e come può essere utile? Scopriamolo insieme.\u003c/p\u003e\n\u003ch2 id=\"-preparazione-del-laboratorio\"\u003e🧪 Preparazione del laboratorio\u003c/h2\u003e\n\u003cp\u003ePer realizzare un caso di studio adeguato, abbiamo bisogno di un programma che vada in crash. Naturalmente al giorno d\u0026rsquo;oggi sono molto rari \u0026#x1f601; quindi ne creeremo uno noi stessi, mettendo in mostra le nostre peggiori abilità e pratiche di programmazione in C.\u003c/p\u003e","title":"SEGFAULT: Debugging di un'applicazione che va in crash"},{"content":"Introduzione: [SYN] Lo scorso 7-8 marzo 2024 ho partecipato all\u0026rsquo;Open Source Day 2024: una conferenza gratuita organizzata dalla Schrodinger-Hat Open Source Community. Due giorni di talk, incontri con vecchi e nuovi amici, networking e divertimento in una città meravigliosa.\nPoiché l\u0026rsquo;evento era suddiviso in due track parallele, ho dovuto scegliere con cura quali seguire; se vi interessa, tutte le sessioni sono state registrate e trasmesse in live streaming.\nGiorno Link alle registrazioni dello streaming 1 Alpha Track Beta Track 2 Alpha Track Beta Track Parlando di eventi open source, ogni proposta di talk era semplicemente una issue su una repository GitHub, e la selezione è stata fatta scegliendo quelle con il maggior numero di voti \u0026quot;thumbs up\u0026quot; (pollice in su) da parte della community.\nPubblicherò qui solo alcuni momenti salienti e riflessioni personali, senza alcuna intenzione di sminuire i talk che non menzionerò \u0026#x1f604;\nPrimo giorno: [SYN-ACK] 📜 PJ Hagerty ha ripercorso la storia dell\u0026rsquo;Open Source: dai tempi ingenui del \u0026quot;tutto è gratis per tutti\u0026quot;, fino allo stato attuale in cui l\u0026rsquo;Open Source è diventato finanziariamente parte integrante di qualsiasi tecnologia. Ottimi spunti su come partecipare ed essere dei bravi cittadini nel mondo Open Source.\n🦀 Irine Kokilashvili ha presentato nanocl, un\u0026rsquo;alternativa a Kubernetes scritta in Rust e nata come progetto di studio.\n♿ Alessandro Albano ha parlato dell\u0026rsquo;importanza di progettare software accessibile e inclusivo.\n🛡️ Abdel Sghiouar ha tenuto un fantastico talk sulla Software Supply Chain Security (SLSA, SBOM), spiegando perché dovremmo preoccuparcene e come funziona in pratica.\n🛰️ Sal Kimmich ci ha portato nello spazio, spiegando come Linux sia stato adattato per l\u0026rsquo;uso in progetti extraterrestri, discutendo sia i progressi tecnici sia le implicazioni più ampie per la community open source.\n👾 Nel pomeriggio sono seguiti due talk su WebAssembly, una tecnologia che si sta diffondendo molto. Uno di Sohan Maheshwar e l\u0026rsquo;altro di Noah Jelich; anche Edoardo Dusi ha parlato di WebAssembly il giorno successivo.\n👔 Infine, Francesco Corti ci ha fatto riflettere su una semplice domanda: dove sta andando l\u0026rsquo;Open Source? Questo modello è sostenibile dal punto di vista del business?\nSecondo giorno: [ACK] 📈 Nathan Marrs ci ha illustrato l\u0026rsquo;arte di monetizzare i progetti open-source senza scendere a compromessi con i loro principi fondamentali, portando come caso di studio la storia di successo di Grafana.\n⌨️ Noam Honig ha tenuto una sessione di live coding mostrando il framework remult, ideale per un\u0026rsquo;integrazione fluida tra backend e frontend e per uno sviluppo rapido di API REST.\n🔮 Christina Dahlén ha presentato lavinmq, un message broker open source progettato per un utilizzo ottimale delle risorse e alte prestazioni. Come sostenitore del linguaggio Crystal, ho apprezzato molto l\u0026rsquo;uso di questo linguaggio spesso sottovalutato \u0026#x1f604;\n🐱 Il team di Schrödinger Hat ha ripercorso la storia del gruppo e ha fatto un bel riassunto delle attività svolte nell\u0026rsquo;ultimo anno.\n🔬 Costa Tsaousis ha presentato una panoramica approfondita sulla creazione di Netdata, una potente pipeline di osservabilità distribuita e open source, progettata per offrire maggiore precisione, scalabilità semplificata e minori costi di gestione rispetto alle soluzioni di monitoraggio tradizionali.\n💾 Federico Terzi ha spiegato i Conflict-free Replicated Data Types (CRDT) in teoria e in pratica. Fantastico e facilissimo da seguire anche per chi non ama JavaScript!\n🎸 Omar Diop ci ha guidato in un viaggio musicale nel mondo dell\u0026rsquo;open source, condividendo la sua esperienza nello sviluppo da zero di un accordatore per chitarra online.\n🎤 Francesco Napoletano ha spiegato in modo brillante come combattere l\u0026rsquo;ageismo, e come uno sviluppatore over 40 possa ancora fare la differenza in 10 semplici passaggi!\nArrivederci [FIN] Un ringraziamento speciale a tutti gli sponsor, in particolare a SUSE per aver reso possibile questa conferenza e la mia partecipazione. Nella foto successiva potete vedere i miei fantastici colleghi Giulio, Luca e Stefano allo stand SUSE. Ottimo lavoro ragazzi!\n","permalink":"https://ilmanzo.github.io/it/post/wrapping_up_osday_2024/","summary":"\u003ch1 id=\"introduzione-syn\"\u003eIntroduzione: [SYN]\u003c/h1\u003e\n\u003cp\u003eLo scorso 7-8 marzo 2024 ho partecipato all\u0026rsquo;\u003ca href=\"https://osday.dev/\"\u003eOpen Source Day 2024\u003c/a\u003e: una conferenza gratuita organizzata dalla \u003ca href=\"https://www.schrodinger-hat.it/\"\u003eSchrodinger-Hat Open Source Community\u003c/a\u003e. Due giorni di talk, incontri con vecchi e nuovi amici, networking e divertimento in \u003ca href=\"https://en.wikipedia.org/wiki/Florence\"\u003euna città meravigliosa\u003c/a\u003e.\u003c/p\u003e\n\u003cp\u003ePoiché l\u0026rsquo;evento era suddiviso in due track parallele, ho dovuto scegliere con cura quali seguire; se vi interessa, \u003ca href=\"https://osday.dev/agenda\"\u003etutte le sessioni sono state registrate e trasmesse in live streaming\u003c/a\u003e.\u003c/p\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003eGiorno\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003eLink alle registrazioni dello streaming\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e1\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ca href=\"https://youtube.com/live/_mA4mvYpU68\"\u003eAlpha Track\u003c/a\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ca href=\"https://youtube.com/live/te8tNwnej0M\"\u003eBeta Track\u003c/a\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e2\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ca href=\"https://youtube.com/live/8owI4xBEIl0\"\u003eAlpha Track\u003c/a\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ca href=\"https://youtube.com/live/Va6vIYCVxj0\"\u003eBeta Track\u003c/a\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003cp\u003eParlando di eventi open source, ogni proposta di talk era semplicemente una issue su una \u003ca href=\"https://github.com/Schrodinger-Hat/osday/issues?q=is%3Aissue\u0026#43;label%3AOSDay-talk\"\u003erepository GitHub\u003c/a\u003e, e la selezione è stata fatta scegliendo quelle con il maggior numero di voti \u0026quot;thumbs up\u0026quot; (pollice in su) da parte della community.\u003c/p\u003e","title":"Resoconto di Osday 2024"},{"content":"Un vecchio ritrovamento Ho ritrovato un vecchio cubieboard3 (cubietruck) che accumulava polvere in un cassetto, quindi ho colto l\u0026rsquo;occasione per provare la cross-compilazione con Rust e raccogliere qui alcune note sul processo. Eccolo qui:\nDiamogli un pinguino Prima di tutto, ho installato una distribuzione Linux ARM su una scheda MicroSD e ho avviato il dispositivo:\n[user@arm ~]$ cat /proc/cpuinfo processor\t: 0 model name\t: ARMv7 Processor rev 4 (v7l) BogoMIPS\t: 50.52 Features\t: half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm CPU implementer\t: 0x41 CPU architecture: 7 CPU variant\t: 0x0 CPU part\t: 0xc07 CPU revision\t: 4 processor\t: 1 model name\t: ARMv7 Processor rev 4 (v7l) BogoMIPS\t: 50.52 Features\t: half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm CPU implementer\t: 0x41 CPU architecture: 7 CPU variant\t: 0x0 CPU part\t: 0xc07 CPU revision\t: 4 Hardware\t: Allwinner sun7i (A20) Family Revision\t: 0000 Quindi il nostro dispositivo ha una CPU ARM modello v7l; questo significa che si tratta di una CPU a 32 bit, e se siete curiosi esiste anche un manuale di riferimento in circolazione. Ora lavoreremo da una macchina di sviluppo.\nDiventiamo \u0026quot;Rustici\u0026quot; Controlliamo il supporto a Rust:\n[x86]$ rustup target list | grep armv7 armv7-linux-androideabi armv7-unknown-linux-gnueabi armv7-unknown-linux-gnueabihf armv7-unknown-linux-musleabi armv7-unknown-linux-musleabihf armv7a-none-eabi armv7r-none-eabi armv7r-none-eabihf Un sacco di opzioni! Per ora lascerei da parte le varianti Android e Musl.\n[x86]$ cargo init tryarm Created binary (application) package [x86]$ cd tryarm [x86]$ cargo build --target armv7-unknown-linux-gnueabihf error: could not compile `tryarm` (bin \u0026#34;tryarm\u0026#34;) due to 1 previous error Riprovare, per favore Sembra non essere sufficiente\u0026hellip; Proviamo lo strumento cross del team Rust Embedded:\n[x86]$ cargo install cross [x86]$ /home/andrea/.cargo/bin/cross build --target armv7-unknown-linux-gnueabihf Trying to pull ghcr.io/cross-rs/armv7-unknown-linux-gnueabihf:0.2.5... Getting image source signatures Copying blob 5b4afa60d436 [===============================\u0026gt;------] 146.3MiB / 172.0MiB | 6.6 M Copying blob 5b4afa60d436 done | Copying blob 58690f9b18fc done | Copying blob da8ef40b9eca done | Copying blob b51569e7c507 done | Copying blob 6c052f8b0b21 done | Copying blob fb15d46c38dc done | Copying blob 5afa4c181482 done | Copying blob b9d42a766612 done | Copying blob cc716323c93e done | Copying blob fe4038eab07b done | Copying blob 4accd797f995 done | Copying blob 3db4794ce9a5 done | Copying blob 8b1f228d2fc0 done | Copying blob 05f315b1fff9 done | Copying blob c0190749220c done | Copying blob 55483985fe64 done | Copying blob df8b7a9f8281 done | Copying blob 9de25b0c2608 done | Copying config 32cf786140 done | Writing manifest to image destination Compiling tryarm v0.1.0 (/project) Finished dev [unoptimized + debuginfo] target(s) in 0.26s Questo strumento scarica un container con la corretta toolchain del compilatore e lo usa per compilare il progetto. Sembra che il nostro programma sia pronto e compilato!\n[x86]$ file target/armv7-unknown-linux-gnueabihf/debug/tryarm target/armv7-unknown-linux-gnueabihf/debug/tryarm: ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=7ff3fc41deb8b4820cc64ff2857cddbfa577111c, with debug_info, not stripped [x86]$ objdump -d target/armv7-unknown-linux-gnueabihf/debug/tryarm 3428: e59d300c ldr r3, [sp, #12] 342c: e0802080 add r2, r0, r0, lsl #1 3430: e08b108b add r1, fp, fp, lsl #1 3434: e1a09003 mov r9, r3 3438: e1a00003 mov r0, r3 343c: e7b91181 ldr r1, [r9, r1, lsl #3]! 3440: e7b02182 ldr r2, [r0, r2, lsl #3]! 3444: e5993004 ldr r3, [r9, #4] 3448: e5904004 ldr r4, [r0, #4] 344c: e0521001 subs r1, r2, r1 3450: e0d41003 sbcs r1, r4, r3 Corri, piccolo, corri Quindi, trasferiamo il nostro binario sul dispositivo ed eseguiamolo:\n[x86]$ scp target/armv7-unknown-linux-gnueabihf/debug/tryarm user@cubietruck:/home/user user@cubietruck\u0026#39;s password: tryarm 100% 3443KB 5.3MB/s 00:00 Passando alla shell del dispositivo:\n[user@arm ~]$ ./tryarm Hello, world! [user@arm ~]$ ldd tryarm linux-vdso.so.1 (0xbefbd000) libgcc_s.so.1 =\u0026gt; /usr/lib/libgcc_s.so.1 (0xb6e40000) librt.so.1 =\u0026gt; /usr/lib/librt.so.1 (0xb6e20000) libpthread.so.0 =\u0026gt; /usr/lib/libpthread.so.0 (0xb6e00000) libdl.so.2 =\u0026gt; /usr/lib/libdl.so.2 (0xb6de0000) libc.so.6 =\u0026gt; /usr/lib/libc.so.6 (0xb6c40000) /lib/ld-linux-armhf.so.3 =\u0026gt; /usr/lib/ld-linux-armhf.so.3 (0xb6eda000) Funziona! \u0026#x1f604;\nIl futuro ci attende Siamo in grado di compilare ed eseguire codice Rust sul nostro piccolo dispositivo, quindi questa Proof of Concept (PoC) è conclusa. In futuro lo useremo per sviluppare e testare software su un\u0026rsquo;architettura diversa.\nAlcune risorse per approfondire:\nhttps://www.modio.se/cross-compiling-rust-binaries-to-armv7.html ","permalink":"https://ilmanzo.github.io/it/post/experiments_with_rust_on_arm_architecture/","summary":"\u003ch3 id=\"un-vecchio-ritrovamento\"\u003eUn vecchio ritrovamento\u003c/h3\u003e\n\u003cp\u003eHo ritrovato un vecchio \u003ca href=\"http://cubieboard.org/tag/cubietruck/\"\u003ecubieboard3 (cubietruck)\u003c/a\u003e che accumulava polvere in un cassetto, quindi ho colto l\u0026rsquo;occasione per provare la cross-compilazione con Rust e raccogliere qui alcune note sul processo. Eccolo qui:\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"cubietruck\" loading=\"lazy\" src=\"/img/cubietruck.jpg\"\u003e\u003c/p\u003e\n\u003ch3 id=\"diamogli-un-pinguino\"\u003eDiamogli un pinguino\u003c/h3\u003e\n\u003cp\u003ePrima di tutto, ho installato una distribuzione Linux ARM su una scheda MicroSD e ho avviato il dispositivo:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#f92672\"\u003e[\u003c/span\u003euser@arm ~\u003cspan style=\"color:#f92672\"\u003e]\u003c/span\u003e$ cat /proc/cpuinfo \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprocessor\t: \u003cspan style=\"color:#ae81ff\"\u003e0\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel name\t: ARMv7 Processor rev \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ev7l\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBogoMIPS\t: 50.52\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eFeatures\t: half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU implementer\t: 0x41\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU architecture: \u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU variant\t: 0x0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU part\t: 0xc07\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU revision\t: \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eprocessor\t: \u003cspan style=\"color:#ae81ff\"\u003e1\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003emodel name\t: ARMv7 Processor rev \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003ev7l\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eBogoMIPS\t: 50.52\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eFeatures\t: half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm \n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU implementer\t: 0x41\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU architecture: \u003cspan style=\"color:#ae81ff\"\u003e7\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU variant\t: 0x0\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU part\t: 0xc07\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eCPU revision\t: \u003cspan style=\"color:#ae81ff\"\u003e4\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eHardware\t: Allwinner sun7i \u003cspan style=\"color:#f92672\"\u003e(\u003c/span\u003eA20\u003cspan style=\"color:#f92672\"\u003e)\u003c/span\u003e Family\n\u003c/span\u003e\u003c/span\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003eRevision\t: \u003cspan style=\"color:#ae81ff\"\u003e0000\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eQuindi il nostro dispositivo ha una CPU ARM modello \u003ccode\u003ev7l\u003c/code\u003e; questo significa che si tratta di una CPU a 32 bit, e se siete curiosi esiste anche un \u003ca href=\"https://dl.linux-sunxi.org/A20/A20%20User%20Manual%202013-03-22.pdf\"\u003emanuale di riferimento\u003c/a\u003e in circolazione. Ora lavoreremo da una macchina di sviluppo.\u003c/p\u003e","title":"Esperimenti con Rust su architettura ARM"},{"content":"🕵️ Introduzione Il Linux Test Project (LTP) è un progetto congiunto avviato anni fa da SGI, OSDL e Bull, sviluppato e oggi mantenuto da IBM, Cisco, Fujitsu, SUSE, Red Hat, Oracle e molti altri. L\u0026rsquo;obiettivo del progetto è fornire alla community open source test che convalidino l\u0026rsquo;affidabilità, la robustezza e la stabilità di Linux.\nIn questi giorni sto esplorando il progetto, quindi con questo articolo voglio mostrare passo dopo passo come configurarlo, come vengono effettivamente scritti i test e fornirvi una guida rapida ed essenziale per scrivere il vostro primo test.\n🧰 Cominciamo NOTA: Poiché alcuni test modificano le impostazioni del sistema operativo, se avete intenzione di eseguire l\u0026rsquo;intera suite di test è consigliabile mantenere pulita la vostra workstation e configurare un ambiente separato, come un PC di riserva o una macchina virtuale di sviluppo.\nLa prima cosa da fare è installare gli strumenti di sviluppo e clonare la repository:\n# zypper in -t pattern devel_basis or # zypper install gcc git make pkg-config autoconf automake bison flex m4 linux-glibc-devel glibc-devel $ git clone https://github.com/linux-test-project/ltp.git \u0026amp;\u0026amp; cd ltp $ make autotools $ ./configure [...omitted output...] Ora potete continuare compilando ed eseguendo un singolo test oppure compilando e installando l\u0026rsquo;intera suite di test. Facciamo un piccolo passo alla volta:\n⚗️ Un test di esempio Se si desidera eseguire solo un singolo test, in realtà non è necessario compilare l\u0026rsquo;intero progetto LTP. Scegliamo un test di esempio per la syscall open(). Per trovare questo test specifico:\n$ cd testcases/kernel/syscalls/open $ cat open03.c Cosa c\u0026rsquo;è dentro? ⁉️ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (c) Linux Test Project, 2001-2024 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. */ /*\\ * [Description] * * Testcase to check open() with O_RDWR | O_CREAT. */ #include \u0026#34;tst_test.h\u0026#34; #define TEST_FILE \u0026#34;testfile\u0026#34; static void verify_open(void) { TST_EXP_FD(open(TEST_FILE, O_RDWR | O_CREAT, 0700)); SAFE_CLOSE(TST_RET); SAFE_UNLINK(TEST_FILE); } static struct tst_test test = { .needs_tmpdir = 1, .test_all = verify_open, }; Questo test è piuttosto semplice, poiché il codice effettivo è inferiore a 10 righe. Ecco una breve panoramica riga per riga:\n1-12: commenti standard, licenza e intestazione della documentazione. Questo progetto ha più di 20 anni di storia! 13: inclusione dell\u0026rsquo;header obbligatorio della libreria LTP 15: un nome di file di esempio che proveremo a creare chiamando la syscall open() 17-19: la funzione di test vera e propria: utilizzando le macro fornite dal framework, invia al kernel una chiamata di sistema open() e si assicura che l\u0026rsquo;operazione vada a buon fine; se per qualsiasi motivo la syscall dovesse restituire un errore, questo viene segnalato automaticamente e il risultato del test viene contrassegnato come fallito (failed). In ogni caso, il valore restituito viene memorizzato nella variabile TST_RET 20-22: le macro SAFE_* ci consentono di chiudere ed eliminare il file appena aperto in modo pulito 24-27: definizione dei metadati del test: di quali opzioni ha bisogno per essere eseguito e qual è la funzione che il framework eseguirà per noi. Il framework LTP cercherà questa struttura e utilizzerà le informazioni contenute al suo interno. Se siete curiosi di conoscere tutte le opzioni disponibili, potete trovare una buona descrizione qui, ma trattandosi di un argomento molto vasto, merita un post dedicato in futuro Il Linux Test Project, così como lo stesso Kernel Linux, fa un uso massiccio di macro C, al fine di mantenere i test puliti, manutenibili e leggibili. Ovviamente tutte le macro e le funzioni di libreria sono documentate e spiegate nella documentazione del progetto.\nPer un riferimento delle syscall, basta consultare le pagine man del sistema. Un piccolo consiglio: è raccomandabile clonare il repository man ufficiale a monte (upstream) perché a volte le pagine man distribuite con le distribuzioni possono essere un po\u0026rsquo; vecchie.\n👟 Come eseguire il test Grazie alla configurazione del build system, possiamo semplicemente compilare con make il nostro singolo test ed eseguire l\u0026rsquo;eseguibile standalone. LTP aggiungerà molte informazioni utili al nostro piccolo programma:\n$ make open03 [... compiler messages omitted...] $ ./open03 tst_test.c:1741: TINFO: LTP version: 20240129 tst_test.c:1625: TINFO: Timeout per run is 0h 00m 30s open03.c:19: TPASS: open(TEST_FILE, O_RDWR | O_CREAT, 0700) returned fd 3 Summary: passed 1 failed 0 broken 0 skipped 0 warnings 0 L\u0026rsquo;eseguibile compilato accetta anche alcune opzioni, sempre grazie al framework LTP:\n$ ./open03 -h Environment Variables --------------------- KCONFIG_PATH Specify kernel config file KCONFIG_SKIP_CHECK Skip kernel config check if variable set (not set by default) LTPROOT Prefix for installed LTP (default: /opt/ltp) LTP_COLORIZE_OUTPUT Force colorized output behaviour (y/1 always, n/0: never) LTP_DEV Path to the block device to be used (for .needs_device) LTP_DEV_FS_TYPE Filesystem used for testing (default: ext2) LTP_SINGLE_FS_TYPE Testing only - specifies filesystem instead all supported (for .all_filesystems) LTP_TIMEOUT_MUL Timeout multiplier (must be a number \u0026gt;=1) LTP_RUNTIME_MUL Runtime multiplier (must be a number \u0026gt;=1) LTP_VIRT_OVERRIDE Overrides virtual machine detection (values: \u0026#34;\u0026#34;|kvm|microsoft|xen|zvm) TMPDIR Base directory for template directory (for .needs_tmpdir, default: /tmp) Timeout and runtime ------------------- Test timeout (not including runtime) 0h 0m 30s Options ------- -h Prints this help -i n Execute test n times -I x Execute test for n seconds -D Prints debug information -V Prints LTP version -C ARG Run child process with ARG arguments (used internally) potete anche controllare il vostro codice sorgente rispetto alle best practice del progetto:\n$ make check-open03 Riceverete suggerimenti ed errori sulla qualità del codice, la formattazione e le possibili deviazioni dagli standard di programmazione del progetto.\n🗿 Hello, nuovo test Quindi, se volete scrivere un nuovo test per LTP, potete semplicemente scegliere una sottocartella in testcases/kernel/ e creare un nuovo file .c usando un template come questo:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #include \u0026lt;tst_test.h\u0026gt; static void setup(void) { // your setup code goes here tst_res(TINFO, \u0026#34;example setup\u0026#34;); } static void cleanup(void) { // your cleanup code goes here tst_res(TINFO, \u0026#34;example cleanup\u0026#34;); } static void run(void) { // your test code goes here tst_res(TPASS, \u0026#34;Doing hardly anything is easy\u0026#34;); } static struct tst_test test = { .test_all = run, .setup = setup, .cleanup = cleanup, }; In questo esempio è importante notare che le funzioni setup() e cleanup() servono a creare/disporre le risorse del test (come buffer, file, socket, processi figli e così via) e vengono eseguite una sola volta, mentre run() è il codice di test vero e proprio e può essere ripetuto molte volte.\nDopo aver salvato il codice sorgente con un nome del tipo mynewtest01.c, basta eseguire:\n$ make mynewtest01 CC testcases/kernel/syscalls/open/mynewtest01 $ ls -l mynewtest* -rwxr-xr-x 1 andrea andrea 738064 Feb 10 11:46 mynewtest01 -rw-r--r-- 1 andrea andrea 475 Feb 10 11:46 mynewtest01.c $ ./mynewtest01 tst_test.c:1741: TINFO: LTP version: 20240129 tst_test.c:1625: TINFO: Timeout per run is 0h 00m 30s mynewtest01.c:5: TINFO: example setup mynewtest01.c:15: TPASS: Doing hardly anything is easy mynewtest01.c:10: TINFO: example cleanup Summary: passed 1 failed 0 broken 0 skipped 0 warnings 0 Non fa ancora nulla ma sembra funzionare; ora non vi resta che implementare la logica del test vero e proprio.\nUna volta terminato e testato, se volete che il nuovo test venga eseguito come parte della suite, dovete aggiungerlo in una delle sottocartelle runtest, dove ogni file di testo definisce un gruppo di test (ovvero una test suite).\n✅ Conclusioni Se siete interessati al progetto, consultate la Wiki del progetto per ulteriore documentazione e per le linee guida di scrittura (Writing Guidelines); potete anche iscrivervi alla Mailing List di LTP. Buon divertimento!\n","permalink":"https://ilmanzo.github.io/it/post/first_steps_of_ltp_linux_test_project/","summary":"\u003ch2 id=\"-introduzione\"\u003e🕵️ Introduzione\u003c/h2\u003e\n\u003cp\u003eIl \u003ca href=\"https://github.com/linux-test-project/ltp\"\u003eLinux Test Project\u003c/a\u003e (LTP) è un progetto congiunto avviato anni fa da SGI, OSDL e Bull, sviluppato e oggi mantenuto da IBM, Cisco, Fujitsu, SUSE, Red Hat, Oracle e molti altri. L\u0026rsquo;obiettivo del progetto è fornire alla community open source test che convalidino l\u0026rsquo;affidabilità, la robustezza e la stabilità di Linux.\u003c/p\u003e\n\u003cp\u003eIn questi giorni sto esplorando il progetto, quindi con questo articolo voglio mostrare passo dopo passo come configurarlo, come vengono effettivamente scritti i test e fornirvi una guida \u003cem\u003erapida ed essenziale\u003c/em\u003e per scrivere il vostro primo test.\u003c/p\u003e","title":"Primi passi con il Linux Test Project"},{"content":"🦀 Introduzione 🦀 Come esercizio, oggi pacchettizzeremo un gioco chiamato battleship-rs sviluppato da Orhun Parmaksız. Useremo inoltre la potenza di openSUSE build service per svolgere la maggior parte del lavoro pesante.\nPrima di iniziare, diamo un\u0026rsquo;occhiata al progetto: è ospitato su GitHub e, se volete provarlo prima di pacchettizzarlo, è un simpatico gioco in cui due persone possono giocare nel terminale tramite una connessione di rete TCP. Il posizionamento iniziale delle navi, il tracciamento dei colpi, i turni dei giocatori e lo stato stesso del gioco sono gestiti da un singolo processo Rust.\nPer la pacchettizzazione vera e propria, seguiremo la documentazione di riferimento sulla wiki di openSUSE.\n📦 Prerequisiti Seguendo le linee guida di OBS, configuriamo il nostro client osc con una configurazione minima:\n$ grep -v \u0026#39;^#\u0026#39; /home/andrea/.config/osc/oscrc [general] apiurl = https://api.opensuse.org ccache = 1 extra-pkgs = vim gdb strace less unzip procps psutils psmisc show_download_progress = 0 [https://api.opensuse.org] user=YOURUSERNAME pass=YOURPASSWORD 🛠️ Configurazione del progetto OBS Ora possiamo spostarci nella nostra directory di sviluppo e creare un sottoprogetto all\u0026rsquo;interno della nostra cartella home:\n$ cd osc $ cd home:amanzini $ osc mkpac battleship-rs A battleship-rs $ cd battleship-rs 🍲 Configurare il sistema di build Per compilare correttamente un pacchetto Rust, abbiamo bisogno di tre elementi:\nun file .spec un file _service un file cargo_config Il primo è il classico .spec di RPM, la ricetta di cui abbiamo bisogno per preparare qualsiasi pacchetto rpm. Sfruttiamo alcune macro per rendere il processo fluido e semplice. Questo mi fa anche notare che non esiste ancora un evidenziatore di sintassi in Hugo per i file spec\u0026hellip;🤨\n$ cat battleship-rs.spec Name: battleship-rs # This will be set by osc services, that will run after this. Version: 0.1.1~0 Release: 0 Summary: Battleship game implemented in Rust. License: MIT Url: https://github.com/orhun/battleship-rs Source0: %{name}-%{version}.tar.zst Source1: vendor.tar.zst Source2: cargo_config BuildRequires: cargo-packaging # Disable this line if you wish to support all platforms. # In most situations, you will likely only target tier1 arches for user facing components. ExclusiveArch: %{rust_tier1_arches} # the name of the actual binary program when differs from the project %define bin_name battleship %description A Battleship game implemented in Rust. Mainly for package practice %prep # The number passed to -a (a stands for \u0026#34;after\u0026#34;) should be equivalent to the Source tag number # of the vendor tarball, 1 in this case (from Source1). %autosetup -p1 -a1 # Remove exec bits to prevent an issue in fedora shebang checking. Uncomment only if required. # find vendor -type f -name \\*.rs -exec chmod -x \u0026#39;{}\u0026#39; \\; %build %{cargo_build} %install # using cargo_install (only supports bindir) # %{cargo_install} # manual process install -D -d -m 0755 %{buildroot}%{_bindir} install -m 0755 %{_builddir}/%{name}-%{version}/target/release/%{bin_name} %{buildroot}%{_bindir}/%{bin_name} # this is useful if you want to run the program internal test suite %check %{cargo_test} %files %{_bindir}/%{bin_name} %license LICENSE %doc README.md %changelog Il secondo elemento è quello in cui avviene la vera magia. Usando questo file di configurazione, OBS è in grado di eseguire molti servizi (services) sul nostro progetto. Prima di tutto, può effettuare il checkout dell\u0026rsquo;esatta versione da git e generare per noi un file .changes con i messaggi di commit. Successivamente, può creare un archivio compresso dei sorgenti ed eseguire un task speciale cargo vendor che si occupa di rendere disponibili offline tutte le nostre dipendenze per la compilazione:\n$ cat _service \u0026lt;services\u0026gt; \u0026lt;service mode=\u0026#34;disabled\u0026#34; name=\u0026#34;obs_scm\u0026#34;\u0026gt; \u0026lt;param name=\u0026#34;url\u0026#34;\u0026gt;https://github.com/orhun/battleship-rs.git\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;versionformat\u0026#34;\u0026gt;@PARENT_TAG@~@TAG_OFFSET@\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;scm\u0026#34;\u0026gt;git\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;revision\u0026#34;\u0026gt;v0.1.1\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;match-tag\u0026#34;\u0026gt;*\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;versionrewrite-pattern\u0026#34;\u0026gt;v(\\d+\\.\\d+\\.\\d+)\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;versionrewrite-replacement\u0026#34;\u0026gt;\\1\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;changesgenerate\u0026#34;\u0026gt;enable\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;changesauthor\u0026#34;\u0026gt;andrea.manzini@suse.com\u0026lt;/param\u0026gt; \u0026lt;/service\u0026gt; \u0026lt;service mode=\u0026#34;disabled\u0026#34; name=\u0026#34;tar\u0026#34; /\u0026gt; \u0026lt;service mode=\u0026#34;disabled\u0026#34; name=\u0026#34;recompress\u0026#34;\u0026gt; \u0026lt;param name=\u0026#34;file\u0026#34;\u0026gt;*.tar\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;compression\u0026#34;\u0026gt;zst\u0026lt;/param\u0026gt; \u0026lt;/service\u0026gt; \u0026lt;service mode=\u0026#34;disabled\u0026#34; name=\u0026#34;set_version\u0026#34;/\u0026gt; \u0026lt;service name=\u0026#34;cargo_vendor\u0026#34; mode=\u0026#34;disabled\u0026#34;\u0026gt; \u0026lt;param name=\u0026#34;src\u0026#34;\u0026gt;battleship-rs\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;compression\u0026#34;\u0026gt;zst\u0026lt;/param\u0026gt; \u0026lt;param name=\u0026#34;update\u0026#34;\u0026gt;true\u0026lt;/param\u0026gt; \u0026lt;/service\u0026gt; \u0026lt;/services\u0026gt; L\u0026rsquo;ultimo elemento di cui abbiamo bisogno è un piccolo file che indica al sistema di build di Rust di utilizzare le dipendenze vendored (locali), invece di scaricarle da Internet.\n$ cat cargo_config [source.crates-io] replace-with = \u0026#34;vendored-sources\u0026#34; [source.vendored-sources] directory = \u0026#34;vendor\u0026#34; Aggiornamento: con la nuova versione, cargo_config viene creato automaticamente e non è più necessario come risorsa esterna, come si può vedere eseguendo il servizio:\n... This rewrite introduces some small changes to how vendoring functions for your package. * cargo_config is no longer created - it\u0026#39;s part of the vendor.tar now * You can safely remove lines related to cargo_config from your spec file ... 🚢 Recupero dei sorgenti a monte e invio a OBS I seguenti comandi provvederanno a:\neseguire i servizi per svolgere i task (questo creerà due archivi .zst) aggiungere tutti i file, inclusa la configurazione, al versionamento di OBS inviare tutto al server di build $ osc service runall $ osc addremove $ osc checkin In teoria abbiamo finito, la compilazione si avvierà su un worker OBS e potremo controllare il log di build; se vogliamo provare tutto localmente, siamo pronti a farlo:\n🏗️ Compilazione locale 🏗️ $ osc build Building battleship-rs.spec for openSUSE_Tumbleweed/x86_64 ... [lots of output omitted] ... build: extracting built packages... RPMS/x86_64/battleship-rs-0.1.1~0-0.x86_64.rpm SRPMS/battleship-rs-0.1.1~0-0.src.rpm OTHER/_statistics OTHER/rpmlint.log 🎮 Proviamo l\u0026rsquo;installazione Dato che abbiamo appena pacchettizzato un gioco, perché non provarlo?\n$ sudo zypper in battleship-rs-0.1.1~0-0.x86_64.rpm Refreshing service \u0026#39;openSUSE\u0026#39;.................................................[done] Loading repository data... Reading installed packages... Resolving package dependencies... The following NEW package is going to be installed: battleship-rs 1 new package to install. Overall download size: 223.4 KiB. Already cached: 0 B. After the operation, additional 574.3 KiB will be used. Continue? [y/n/v/...? shows all options] (y): Retrieving: battleship-rs-0.1.1~0-0.x86_64 (Plain RPM files cache) (1/1), 223.4 KiB battleship-rs-0.1.1~0-0.x86_64.rpm: Package header is not signed! battleship-rs-0.1.1~0-0.x86_64 (Plain RPM files cache): Signature verification failed [6-File is unsigned] Abort, retry, ignore? [a/r/i] (a): i Checking for file conflicts: .................................................[done] (1/1) Installing: battleship-rs-0.1.1~0-0.x86_64 .............................[done] Running post-transaction scripts .............................................[done] ora il pacchetto è installato, possiamo provarlo; sul \u0026quot;server\u0026quot; vedremo il campo di battaglia e le connessioni dei client:\n$ battleship [+] Server is listening on 127.0.0.1:1234 [+] New connection: 127.0.0.1:33692 [+] New connection: 127.0.0.1:41104 [#] Andrea\u0026#39;s grid: A B C D E F G H I J 1 • • • • • • • • • • 2 • • • • • • ▭ ▭ • • 3 • • • • • • • ▭ ▭ • 4 • • • • • • • • • • 5 • ▧ ▧ • • • • • • • 6 • ▧ ▧ • • • • • • • 7 • ▧ ▧ • • • • • • • 8 • • • • • • • • • • 9 • • △ △ ▭ ▭ ▭ ▭ • • 10 • • • • • • • • • • [#] ilmanzo\u0026#39;s grid: A B C D E F G H I J 1 • • • • • • • • • • 2 • • • • • • • • • 10 3 • • • • • • • • • 10 4 ▭ ▭ • • • • • • • • 5 • • • • • • • • 10 • 6 • • • • • • • • 10 • 7 • • • • • • ▭ ▭ • • 8 • • • • • • • • • • 9 • • • • • • • • • • 10 • • • • • • • • • • [#] Game is starting. [#] Andrea\u0026#39;s turn. per giocare sul serio, dobbiamo aprire due terminali diversi e contattare il server, non è permesso barare :)\n$ nc 127.0.0.1 1234 _ _ __|_|__|_|__ _|____________|__ |o o o o o o o o / ~\u0026#39;`~\u0026#39;`~\u0026#39;`~\u0026#39;`~\u0026#39;`~\u0026#39;`~\u0026#39;`~ Welcome to Battleship! Please enter your name: ilmanzo Your opponent is Andrea Game starts in 3... Game starts in 2... Game starts in 1... A B C D E F G H I J 1 • • • • • • • • • • 2 • • • • • • • • • • 3 • • • • • • • • • • 4 • • • • • • • • • • 5 • • • • • • • • • • 6 • • • • • • • • • • 7 • • • • • • • • • • 8 • • • • • • • • • • 9 • • • • • • • • • • 10 • • • • • • • • • • A B C D E F G H I J 1 • • • • • • • • • • 2 • • • • • • • • • 10 3 • • • • • • • • • 10 4 ▭ ▭ • • • • • • • • 5 • • • • • • • • 10 • 6 • • • • • • • • 10 • 7 • • • • • • ▭ ▭ • • 8 • • • • • • • • • • 9 • • • • • • • • • • 10 • • • • • • • • • • Andrea\u0026#39;s turn. 🎇 Considerazioni finali 🎇 Prima di tutto, grazie a Orhun Parmaksız per aver scritto questo fantastico gioco da terminale!\nSe volete migliorare come manutentori di pacchetti, non perdetevi questa eccellente guida di Michael Vetter.\nUlteriori dettagli sulla storia e sulle scelte dietro la pacchettizzazione di Rust in openSUSE si trovano nel talk di William Brown alla RustConf 2022 Altri tutorial sulla pacchettizzazione sono disponibili sul canale YouTube di openSUSE: https://www.youtube.com/opensuse\nPotete trovare tutti i file e il progetto nella mia cartella home sull\u0026rsquo;openSUSE build service. Buon hacking!\n","permalink":"https://ilmanzo.github.io/it/post/introduction_to_packaging_rust_applications/","summary":"\u003ch2 id=\"-introduzione-\"\u003e🦀 Introduzione 🦀\u003c/h2\u003e\n\u003cp\u003eCome esercizio, oggi pacchettizzeremo un gioco chiamato \u003ccode\u003ebattleship-rs\u003c/code\u003e sviluppato da \u003ca href=\"https://orhun.dev/\"\u003eOrhun Parmaksız\u003c/a\u003e. Useremo inoltre la potenza di \u003ca href=\"https://build.opensuse.org/\"\u003eopenSUSE build service\u003c/a\u003e per svolgere la maggior parte del lavoro pesante.\u003c/p\u003e\n\u003cp\u003ePrima di iniziare, diamo un\u0026rsquo;occhiata al progetto: è ospitato su \u003ca href=\"https://github.com/orhun/battleship-rs\"\u003eGitHub\u003c/a\u003e e, se volete provarlo prima di pacchettizzarlo, è un simpatico gioco in cui due persone possono giocare nel terminale tramite una connessione di rete TCP. Il posizionamento iniziale delle navi, il tracciamento dei colpi, i turni dei giocatori e lo stato stesso del gioco sono gestiti da un singolo processo Rust.\u003c/p\u003e","title":"Introduzione al packaging delle applicazioni Rust"},{"content":"Introduzione Questo articolo fa seguito al mio post precedente ed è una sorta di continuazione della serie sull\u0026rsquo;argomento, in cui esploriamo modi per rendere il nostro sistema di test più \u0026quot;inaffidabile\u0026quot; al fine di osservare se le nostre applicazioni si comportano correttamente in ambienti difficili e non ideali.\nIn questo articolo esploreremo alcune tecnologie Linux:\nNetwork Namespace (netns) Dispositivi Ethernet Virtuali (veth) Politica di scheduling per l\u0026rsquo;emulazione di rete (netem) L\u0026rsquo;obiettivo è configurare un collegamento di rete virtuale all\u0026rsquo;interno del nostro sistema, far comunicare i due dispositivi di rete tra loro e quindi simulare una comunicazione scadente/lenta/instabile/difettosa per testare come si comportano le applicazioni in condizioni difficili.\nPronti a giocare e a rompere qualcosa?\nCrediti immagine: Abdulvahap Demir\nConfigurazione dei netns I network namespace rappresentano una tecnologia fondamentale ed essenziale per i container, in quanto consentono di stabilire ambienti di rete segregati all\u0026rsquo;interno di un sistema Linux. Facilitano la creazione di stack di rete distinti per i processi, incluse interfacce, tabelle di routing e regole del firewall. Questa segregazione garantisce che i processi all\u0026rsquo;interno di un network namespace rimangano separati e isolati da quelli in altri namespace.\nper creare e gestire i netns abbiamo solo bisogno del comando ip:\n$ ip netns add ns_1 $ ip netns add ns_2 Con questi comandi abbiamo appena configurato uno spazio vuoto, ora dobbiamo inserirci qualcosa dentro.\nConfigurazione delle ethernet virtuali I dispositivi veth, acronimo di virtual Ethernet, sono interfacce di rete virtuali doppie impiegate per collegare i network namespace. Ogni coppia è composta da due endpoint: uno all\u0026rsquo;interno di uno specifico namespace e l\u0026rsquo;altro in un namespace separato. Queste interfacce virtuali imitano i cavi Ethernet, consentendo una comunicazione fluida tra i namespace interconnessi. Il traffico può attraversare questa coppia veth in modo bidirezionale, facilitando la trasmissione a due vie.\n$ ip link add veth_1 type veth peer name veth_2 $ ip link set veth_1 netns ns_1 $ ip link set veth_2 netns ns_2 $ ip netns exec ns_1 ip link set dev veth_1 up $ ip netns exec ns_2 ip link set dev veth_2 up nota: ip netns ns_1 exec COMANDO è una comoda scorciatoia per eseguire un singolo comando in uno specifico namespace.\nAll\u0026rsquo;interno della vostra macchina ci saranno ora due nuovi namespace indipendenti, ciascuno con la propria scheda di rete virtuale, totalmente separati dall\u0026rsquo;ambiente dell\u0026rsquo;host:\n┌──────────────────────────────────────────────────────────┐ │ Linux machine │ │ │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ ns_1 │ │ ns_2 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ┌─────────┤ ├─────────┐ │ │ │ │ │ │◄───────────┤ │ │ │ │ │ │ veth_1 │ │ veth_2 │ │ │ │ │ │ ├───────────►│ │ │ │ │ │ └─────────┤ ├─────────┘ │ │ │ │ │ │ │ │ │ └──────────────┘ └──────────────┘ │ │ │ │ │ └──────────────────────────────────────────────────────────┘ Indirizzamento Finora i dispositivi virtuali non hanno ancora alcun indirizzo IP, e anche il loopback è disattivato:\n$ ip -all netns exec ip link show netns: ns_1 1: lo: \u0026lt;LOOPBACK\u0026gt; mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 9: veth_1@if8: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000 link/ether 52:69:cf:de:7d:10 brd ff:ff:ff:ff:ff:ff link-netns ns_2 netns: ns_2 1: lo: \u0026lt;LOOPBACK\u0026gt; mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 8: veth_2@if9: \u0026lt;BROADCAST,MULTICAST,UP,LOWER_UP\u0026gt; mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000 link/ether 6e:19:3c:20:e0:9a brd ff:ff:ff:ff:ff:ff link-netns ns_1 assegniamo loro un IPV4 casuale sulla stessa subnet:\n$ ip netns exec ns_1 ip addr add 10.1.1.1/24 dev veth_1 $ ip netns exec ns_2 ip addr add 10.1.1.2/24 dev veth_2 La cosa fantastica ora è che possiamo raggiungere l\u0026rsquo;altra estremità solo tramite il namespace. Giusto per essere chiari, questo non funzionerà:\n$ ping -c 3 10.1.1.2 PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data. --- 10.1.1.2 ping statistics --- 3 packets transmitted, 0 received, 100% packet loss, time 2020ms Perché? Perché dobbiamo eseguire il comando ping dal namespace corretto:\n$ ip netns exec ns_1 ping -c 3 10.1.1.2 PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data. 64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.040 ms 64 bytes from 10.1.1.2: icmp_seq=2 ttl=64 time=0.044 ms 64 bytes from 10.1.1.2: icmp_seq=3 ttl=64 time=0.057 ms --- 10.1.1.2 ping statistics --- 3 packets transmitted, 3 received, 0% packet loss, time 2021ms rtt min/avg/max/mdev = 0.040/0.047/0.057/0.007 ms Guardando quei numeri di RTT (round-trip time), questa rete virtuale sembra funzionare in modo rapido e fluido, quindi è ora di rompere qualcosa\u0026hellip; 😈\nFault injection Aggiungiamo un ritardo casuale di 50ms ± 25ms a ciascun pacchetto su un lato:\n$ ip netns exec ns_1 tc qdisc add dev veth_1 root netem delay 50ms 25ms dall\u0026rsquo;altro lato, simuliamo anche una probabilità del 50% di pacchetti persi, con una probabilità del 25% di perdita dei pacchetti successivi (per emulare perdite di pacchetti a raffica o a burst):\n$ ip netns exec ns_2 tc qdisc add dev veth_2 root netem loss 50% 25% Come si comporterà il ping? Decisamente male: 👎\n$ ip netns exec ns_1 ping -c 10 10.1.1.2 PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data. 64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=66.6 ms 64 bytes from 10.1.1.2: icmp_seq=3 ttl=64 time=34.6 ms 64 bytes from 10.1.1.2: icmp_seq=4 ttl=64 time=41.6 ms 64 bytes from 10.1.1.2: icmp_seq=6 ttl=64 time=28.0 ms 64 bytes from 10.1.1.2: icmp_seq=9 ttl=64 time=51.6 ms 64 bytes from 10.1.1.2: icmp_seq=10 ttl=64 time=50.8 ms --- 10.1.1.2 ping statistics --- 10 packets transmitted, 6 received, 40% packet loss, time 9081ms rtt min/avg/max/mdev = 28.031/45.522/66.569/12.561 ms Altre due fantastiche funzionalità di netem sono la corruzione dei pacchetti (Packet corruption), che simula l\u0026rsquo;errore di un singolo bit a un offset casuale nel pacchetto, e il riordinamento dei pacchetti (Packet Re-ordering), che fa sì che una certa percentuale di pacchetti arrivi in ordine errato. Per qualsiasi dettaglio, potete consultare la pagina man di tc-netem(8).\nConclusioni e pulizia Siamo arrivati ad avere una rete simulata in cui possiamo controllare la perdita di pacchetti e il ritardo / jitter; possiamo fare qualsiasi esperimento necessario eseguendo i nostri servizi nel namespace corretto.\nAl termine, se non abbiamo altri namespace definiti, è semplicissimo rimuovere ogni traccia dal nostro sistema con un singolo comando:\n$ ip --all netns del ","permalink":"https://ilmanzo.github.io/it/post/faulty_network_simulation/","summary":"\u003ch2 id=\"introduzione\"\u003eIntroduzione\u003c/h2\u003e\n\u003cp\u003eQuesto articolo fa seguito al mio \u003ca href=\"https://ilmanzo.github.io/it/post/faulty_disk_simulation/\"\u003epost precedente\u003c/a\u003e ed è una sorta di continuazione della serie sull\u0026rsquo;argomento, in cui esploriamo modi per rendere il nostro sistema di test più \u0026quot;inaffidabile\u0026quot; al fine di osservare se le nostre applicazioni si comportano correttamente in ambienti difficili e non ideali.\u003c/p\u003e\n\u003cp\u003eIn questo articolo esploreremo alcune tecnologie Linux:\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eNetwork Namespace (\u003cstrong\u003enetns\u003c/strong\u003e)\u003c/li\u003e\n\u003cli\u003eDispositivi Ethernet Virtuali (\u003cstrong\u003eveth\u003c/strong\u003e)\u003c/li\u003e\n\u003cli\u003ePolitica di scheduling per l\u0026rsquo;emulazione di rete (\u003cstrong\u003enetem\u003c/strong\u003e)\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eL\u0026rsquo;obiettivo è configurare un collegamento di rete virtuale all\u0026rsquo;interno del nostro sistema, far comunicare i due dispositivi di rete tra loro e quindi simulare una comunicazione \u003cem\u003escadente/lenta/instabile/difettosa\u003c/em\u003e per testare come si comportano le applicazioni in condizioni difficili.\u003c/p\u003e","title":"Fault Injection in Network Namespace e ambienti Veth"},{"content":"\u0026quot;Sembri un disco rotto\u0026quot; È un modo di dire comune quando qualcuno ripete continuamente gli stessi concetti. Ma a volte anche un disco rotto (o danneggiato) può rivelarsi utile.\nATTENZIONE: Nessun filesystem o dispositivo è stato danneggiato durante la realizzazione di questo esperimento 😉\nCrediti immagine: Mick Haupt\nIn questo articolo vorrei esplorare i potenti strumenti di cui disponiamo in Linux per simulare la gestione di dischi danneggiati, ovvero unità che segnalano errori in modo più o meno casuale. Perché è importante? Perché simulando errori che prima o poi si verificheranno anche nel mondo reale, siamo in grado di creare software più robusto, capace di resistere a eventuali problemi dell\u0026rsquo;infrastruttura.\nConfigurazione Per evitare problemi nel nostro sistema di sviluppo e per rendere il processo il più portabile possibile, iniziamo creando un disco fittizio da 1 GB basato su un loop device.\n# dd if=/dev/zero of=/myfakedisk.bin bs=1M count=1024 1024+0 records in 1024+0 records out 1073741824 bytes (1.1 GB, 1.0 GiB) copied, 0.446898 s, 2.4 GB/s # losetup /dev/loop0 /myfakedisk.bin Ora possiamo usare the loop device proprio come qualsiasi altro block device: possiamo creare un filesystem e montarlo\n# mkfs.ext4 /dev/loop0 mke2fs 1.46.4 (18-Aug-2021) Discarding device blocks: done Creating filesystem with 262144 4k blocks and 65536 inodes Filesystem UUID: bcba505c-54fa-49e5-852c-b5ea3faa53d0 Superblock backups stored on blocks: 32768, 98304, 163840, 229376 Allocating group tables: done Writing inode tables: done Creating journal (8192 blocks): done Writing superblocks and filesystem accounting information: done # mkdir /mnt/good \u0026amp;\u0026amp; mount /dev/loop0 /mnt/good \u0026amp;\u0026amp; echo \u0026#34;test\u0026#34; \u0026gt; /mnt/good/test.txt \u0026amp;\u0026amp; umount /mnt/good il nostro \u0026quot;disco virtuale\u0026quot; funzionante è pronto; ora possiamo crearne uno difettoso utilizzando le funzionalità del device mapper di Linux.\nCos\u0026rsquo;è il device mapper? Crediti immagine: Monstera Production\nIl Device Mapper di Linux è un framework a livello di kernel che consente la creazione di block device virtuali mappando dispositivi di storage fisici o volumi logici a questi dispositivi virtuali. Opera all\u0026rsquo;interno del kernel Linux, fornendo uno strato per creare, gestire e manipolare dispositivi di storage attraverso varie tecniche di mappatura come mirroring, striping, crittografia e snapshot. Questo framework consente l\u0026rsquo;implementazione di funzionalità di storage avanzate come la gestione dei volumi, il RAID e il thin provisioning, offrendo maggiore flessibilità, scalabilità e affidabilità nella gestione delle risorse di storage all\u0026rsquo;interno del sistema operativo Linux.\nIn pratica, creeremo una \u0026quot;mappa\u0026quot; tra il nostro dispositivo funzionante e uno \u0026quot;nuovo\u0026quot;, con questo schema approssimativo:\ndal settore 0 a 2047, recupera i dati dal dispositivo sottostante (poiché non vogliamo interferire con la tabella delle partizioni e i metadati) dal settore 2048 a metà della dimensione del disco, restituisce un errore, oppure i dati originali, con una probabilità di fallimento del 20% da metà dimensione alla fine, restituisce nuovamente i dati dal dispositivo sottostante La dimensione del disco può essere trovata con un semplice controllo:\n# cat /sys/block/loop0/size 2097152 Questo tipo di mappatura si esprime nel comando dmsetup create:\n# dmsetup create bad_disk \u0026lt;\u0026lt; EOF 0 2048 linear /dev/loop0 0 2048 1047552 flakey /dev/loop0 2048 4 1 1049600 1047552 linear /dev/loop0 1049600 EOF # ls -l /dev/mapper/bad_disk lrwxrwxrwx 1 root root 7 Nov 19 17:51 /dev/mapper/bad_disk -\u0026gt; ../dm-0 Per ogni voce della tabella dobbiamo specificare:\nsettore iniziale/offset della mappatura dimensione della mappatura quale mapper utilizzare opzioni del mapper (per i dettagli fare riferimento alla documentazione) In questa configurazione stiamo utilizzando il mapper linear e quello flakey. Altri mapper utili possono essere delay per simulare dischi molto lenti o dust che emula il comportamento dei settori danneggiati in posizioni arbitrarie, offrendo anche la possibilità di abilitare l\u0026rsquo;emulazione dei guasti in un momento arbitrario.\nProviamolo! Il nostro disco di supporto è già formattato, quindi è il momento di provare quello difettoso, montandolo e scrivendoci sopra dei dati:\n# mkdir /mnt/bad \u0026amp;\u0026amp; mount /dev/mapper/bad_disk /mnt/bad \u0026amp;\u0026amp; cd /mnt/bad # df -h | grep -E \u0026#39;(^Filesystem|bad)\u0026#39; Filesystem Size Used Avail Use% Mounted on /dev/mapper/bad_disk 974M 28K 907M 1% /mnt/bad # while sleep 1 ; do dd if=/dev/zero of=trytowrite.bin bs=1M count=500 ; done 500+0 records in 500+0 records out 524288000 bytes (524 MB, 500 MiB) copied, 0.595353 s, 881 MB/s 500+0 records in 500+0 records out 524288000 bytes (524 MB, 500 MiB) copied, 0.637194 s, 823 MB/s Message from syslogd@localhost at Nov 19 18:09:15 ... kernel:[ 8017.117593][T23594] EXT4-fs (dm-0): failed to convert unwritten extents to written extents -- potential data loss! (inode 13, error -30) Message from syslogd@localhost at Nov 19 18:09:15 ... kernel:[ 8017.118445][T23976] EXT4-fs (dm-0): failed to convert unwritten extents to written extents -- potential data loss! (inode 13, error -30) dd: error writing \u0026#39;trytowrite.bin\u0026#39;: Read-only file system 481+0 records in 480+0 records out 503865344 bytes (504 MB, 481 MiB) copied, 0.549939 s, 916 MB/s dd: failed to open \u0026#39;trytowrite.bin\u0026#39;: Read-only file system dd: failed to open \u0026#39;trytowrite.bin\u0026#39;: Read-only file system dd: failed to open \u0026#39;trytowrite.bin\u0026#39;: Read-only file system dd: failed to open \u0026#39;trytowrite.bin\u0026#39;: Read-only file system dd: failed to open \u0026#39;trytowrite.bin\u0026#39;: Read-only file system Il fallimento del disco è un successo! Come possiamo vedere, all\u0026rsquo;inizio alcune operazioni di I/O vanno a buon fine, poi il disco fallisce e nel log di dmesg possiamo trovare maggiori dettagli:\n[ 7962.645178] EXT4-fs (dm-0): error loading journal [ 7979.334186] EXT4-fs (dm-0): mounted filesystem with ordered data mode. Opts: (null). Quota mode: none. [ 8016.759602] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 129024) [ 8016.759641] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 129280) [ 8016.759685] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 129536) [ 8016.759802] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 129870) [ 8016.760119] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 130625) [ 8016.760122] Buffer I/O error on device dm-0, logical block 130625 [ 8016.760129] Buffer I/O error on device dm-0, logical block 130626 [ 8016.760131] Buffer I/O error on device dm-0, logical block 130627 [ 8016.760132] Buffer I/O error on device dm-0, logical block 130628 [ 8016.760133] Buffer I/O error on device dm-0, logical block 130629 [ 8016.760134] Buffer I/O error on device dm-0, logical block 130630 [ 8016.760135] Buffer I/O error on device dm-0, logical block 130631 [ 8016.760136] Buffer I/O error on device dm-0, logical block 130632 [ 8016.760137] Buffer I/O error on device dm-0, logical block 130633 [ 8016.760138] Buffer I/O error on device dm-0, logical block 130634 [ 8016.923667] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 54272) [ 8016.923731] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 54783) [ 8016.924020] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 55296) [ 8016.924335] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 60416) [ 8016.924394] EXT4-fs warning (device dm-0): ext4_end_bio:347: I/O error 10 writing to inode 13 starting block 61803) [ 8016.961108] Buffer I/O error on dev dm-0, logical block 131103, lost sync page write [ 8016.961125] Aborting journal on device dm-0-8. [ 8016.961127] Buffer I/O error on dev dm-0, logical block 131072, lost sync page write [ 8016.961128] JBD2: Error -5 detected when updating journal superblock for dm-0-8. [ 8016.961142] EXT4-fs error (device dm-0): ext4_journal_check_start:83: comm kworker/u2:3: Detected aborted journal [ 8016.966200] EXT4-fs error (device dm-0): ext4_journal_check_start:83: comm dd: Detected aborted journal In senso più generale, questi concetti rientrano nel principio del \u0026quot;chaos engineering\u0026quot;. Questa può anche essere una buona esercitazione per amministratori di sistema junior che desiderano imparare ad affrontare un filesystem danneggiato e tentare di recuperare i dati.\nPulizia Per rimuovere le tracce dei nostri esperimenti, è sufficiente smontare il disco \u0026quot;difettoso\u0026quot;, rimuovere la mappatura e scollegare il loop device dal file di supporto.\n# umount /mnt/bad \u0026amp;\u0026amp; rmdir /mnt/bad # dmsetup remove bad_disk # losetup -d /dev/loop0 ","permalink":"https://ilmanzo.github.io/it/post/faulty_disk_simulation/","summary":"\u003ch2 id=\"sembri-un-disco-rotto\"\u003e\u003cem\u003e\u0026quot;Sembri un disco rotto\u0026quot;\u003c/em\u003e\u003c/h2\u003e\n\u003cp\u003eÈ un modo di dire comune quando qualcuno ripete continuamente gli stessi concetti. Ma a volte anche un disco rotto (o danneggiato) può rivelarsi utile.\u003c/p\u003e\n\u003cp\u003eATTENZIONE: Nessun filesystem o dispositivo è stato danneggiato durante la realizzazione di questo esperimento 😉\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"broken-record\" loading=\"lazy\" src=\"/img/pexels-mick-haupt-7663550.jpg\"\u003e\nCrediti immagine: \u003ca href=\"https://www.pexels.com/@mickhaupt/\"\u003eMick Haupt\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003eIn questo articolo vorrei esplorare i potenti strumenti di cui disponiamo in Linux per simulare la gestione di dischi danneggiati, ovvero unità che segnalano errori in modo più o meno casuale.\nPerché è importante? Perché simulando errori che prima o poi si verificheranno anche nel mondo reale, siamo in grado di creare software più robusto, capace di resistere a eventuali problemi dell\u0026rsquo;infrastruttura.\u003c/p\u003e","title":"Aspettati l'inaspettato"},{"content":"Introduzione 🚌 D-Bus è un sistema di message bus e uno standard per l\u0026rsquo;inter-process communication (IPC), utilizzato principalmente nelle applicazioni desktop Linux. Sia Qt che GLib offrono astrazioni di alto livello per la comunicazione D-Bus, e molti dei servizi desktop su cui facciamo affidamento esportano protocolli D-Bus. Anche l\u0026rsquo;onnipresente systemd può essere interfacciato solo tramite l\u0026rsquo;API D-Bus. Tuttavia, D-Bus ha i suoi difetti, in particolare la mancanza di documentazione aggiornata. In questo articolo esploreremo come scrivere il nostro servizio D-Bus in Rust e collegarlo a un client D-Bus sempre scritto in Rust.\nPer iniziare, se volete fare un po\u0026rsquo; di pratica con D-Bus, vi consiglio questo tutorial e questo link se volete rinfrescarvi la memoria sulla nomenclatura e sui concetti di D-Bus.\nTutto il codice si trova nel suo repository GitHub, così potrete seguirlo e provarlo voi stessi. Buon viaggio!\nCrediti immagine: Nubia Navarro\nIl Servizio Il servizio che esporremo sarà molto semplice: una volta chiamato, terrà traccia di quante volte è stato invocato e dell\u0026rsquo;ultima data/ora di chiamata. Potremo anche passare il nostro nome come parametro, giusto per mostrare come funziona il passaggio dei parametri.\nGrazie a una fantastica crate Rust, creare un servizio D-Bus è piuttosto semplice. Abbiamo scelto di renderlo asincrono, perché\u0026hellip; Perché no?\nLa funzione principale risiede nell\u0026rsquo;implementazione del trait:\nstruct MyService { call_count: u64, call_timestamp: Option\u0026lt;DateTime\u0026lt;Local\u0026gt;\u0026gt;, } #[dbus_interface(name = \u0026#34;org.zbus.MyService\u0026#34;)] impl MyService { async fn call_me(\u0026amp;mut self, name: \u0026amp;str) -\u0026gt; String { let msg = match self.call_count { 0 =\u0026gt; format!(\u0026#34;Hi {}, this is the first time you call me!\u0026#34;, name), _ =\u0026gt; format!( \u0026#34;Hello {}, I have been called {} times, last was at {}\u0026#34;, name, self.call_count, self.call_timestamp .expect(\u0026#34;unable to get local time\u0026#34;) .to_rfc2822() ), }; self.call_count += 1; self.call_timestamp = Some(Local::now()); msg } } Proviamolo! Compiliamo l\u0026rsquo;intero progetto con:\n$ cargo build --release Successivamente possiamo eseguire il binario del servizio, che rimarrà in attesa di connessioni:\n$ target/release/service Con il servizio in esecuzione, possiamo ispezionarlo e interrogarlo utilizzando qualsiasi client D-Bus, come D-Feet o busctl:\n$ SVC=org.zbus.MyService $ busctl --user call $SVC /org/zbus/MyService $SVC CallMe s \u0026#34;Andrea\u0026#34; s \u0026#34;Hi Andrea, this is the first time you call me!\u0026#34; $ busctl --user call $SVC /org/zbus/MyService $SVC CallMe s \u0026#34;Andrea\u0026#34; s \u0026#34;Hello Andrea, I have been called 1 times, last was at Tue, 3 Oct 2023 18:08:16 +0200\u0026#34; $ busctl --user call $SVC /org/zbus/MyService $SVC CallMe s \u0026#34;Andrea\u0026#34; s \u0026#34;Hello Andrea, I have been called 2 times, last was at Tue, 3 Oct 2023 18:09:43 +0200\u0026#34; Potete notare la piccola lettera s prima del parametro del nome. Si tratta della dichiarazione del tipo di parametro, che segue il type system di D-Bus.\nBene, il nostro servizio sembra funcionarci. Potremmo fermarci qui, ma implementiamo anche\u0026hellip;\nIl Client Un semplice client è comodo da avere e aiuterà molto quando vorremo aggiungere test funzionali al nostro progetto. Inoltre, ci permette di fare pratica con una bella funzionalità di Cargo chiamata workspaces, utile per ospitare più binari all\u0026rsquo;interno dello stesso progetto.\nDato che il codice sorgente è breve, possiamo incollarlo interamente:\nuse zbus::{dbus_proxy, Connection, Result}; #[dbus_proxy( interface = \u0026#34;org.zbus.MyService\u0026#34;, default_service = \u0026#34;org.zbus.MyService\u0026#34;, default_path = \u0026#34;/org/zbus/MyService\u0026#34; )] trait MyService { async fn call_me(\u0026amp;self, name: \u0026amp;str) -\u0026gt; Result\u0026lt;String\u0026gt;; } #[tokio::main] async fn main() -\u0026gt; Result\u0026lt;()\u0026gt; { let connection = Connection::session().await?; // `dbus_proxy` macro creates `MyServiceProxy` based on `Notifications` trait. let proxy = MyServiceProxy::new(\u0026amp;connection).await?; let reply = proxy.call_me(\u0026#34;Andrea\u0026#34;).await?; println!(\u0026#34;{reply}\u0026#34;); Ok(()) } Dovreste averlo già compilato, quindi, con il servizio in esecuzione, basta lanciare:\n$ target/release/client \u0026#34;Hello Andrea, I have been called 3 times, last was at Tue, 3 Oct 2023 18:19:26 +0200\u0026#34; Sembra che per ora possiamo parcheggiare il bus e terminare qui il nostro viaggio 😊\nCrediti immagine: Hans Middendorp\nConclusioni L\u0026rsquo;implementazione di riferimento a basso livello delle API di D-Bus e il protocollo D-Bus sono stati ampiamente testati nel mondo reale per diversi anni e sono ormai \u0026quot;scolpiti nella pietra\u0026quot;. Eventuali modifiche future saranno compatibili o opportunamente versionate.\nD-Bus è una tecnologia che ha circa 15 anni, ma è ancora ampiamente utilizzata. Sfortunatamente molti documenti in circolazione sono datati o fuorvianti, quindi può essere utile rinfrescare un po\u0026rsquo; i concetti e giocare con questo sistema di message bus. Buon hacking!\n","permalink":"https://ilmanzo.github.io/it/post/a_trip_on_the_rusty_dbus/","summary":"\u003ch2 id=\"introduzione-\"\u003eIntroduzione 🚌\u003c/h2\u003e\n\u003cp\u003e\u003ca href=\"https://www.freedesktop.org/wiki/Software/dbus/\"\u003eD-Bus\u003c/a\u003e è un sistema di message bus e uno standard per l\u0026rsquo;inter-process communication (IPC), utilizzato principalmente nelle applicazioni desktop Linux. Sia \u003ca href=\"https://doc.qt.io/qt-6/qtdbus-index.html\"\u003eQt\u003c/a\u003e che \u003ca href=\"https://dbus.freedesktop.org/doc/dbus-glib/\"\u003eGLib\u003c/a\u003e offrono astrazioni di alto livello per la comunicazione D-Bus, e molti dei servizi desktop su cui facciamo affidamento esportano protocolli D-Bus. Anche l\u0026rsquo;onnipresente \u003cstrong\u003esystemd\u003c/strong\u003e può essere interfacciato solo tramite l\u0026rsquo;API D-Bus. Tuttavia, D-Bus ha i suoi difetti, in particolare la mancanza di documentazione aggiornata. In questo articolo esploreremo come scrivere il nostro servizio D-Bus in Rust e collegarlo a un client D-Bus sempre scritto in Rust.\u003c/p\u003e","title":"Un viaggio sulla rugginosa D-Bus"},{"content":"Intro Il portatile Thinkpad P15 è un\u0026rsquo;ottima macchina Linux, ma c\u0026rsquo;è un dettaglio fastidioso, come scrive la Wiki di Arch: \u0026ldquo;Il funzionamento predefinito delle ventole è rumoroso, poiché sono praticamente sempre a media potenza. Il programma thinkfan può essere utilizzato per ottenere un funzionamento più silenzioso, pur mantenendo temperature ragionevoli.\u0026rdquo; . Rendiamolo più silenzioso.\nPrerequisiti Installare il pacchetto rpm thinkfan e abilitare il demone:\n# zypper in thinkfan \u0026amp;\u0026amp; systemctl enable --now thinkfan Assicurarsi che i moduli vengano caricati all\u0026rsquo;avvio con le opzioni per sovrascrivere il controllo delle ventole e abilitare le funzionalità sperimentali:\n$ cat /etc/modules-load.d/thinkpad.conf thinkpad_acpi coretemp $ cat /etc/modprobe.d/thinkpad_acpi.conf options thinkpad_acpi fan_control=1 experimental=1 Configurazione La configurazione del demone consiste in un singolo e breve file. Nella prima parte dobbiamo specificare il virtual file contenente le temperature; poi il file che controlla la velocità della ventola, e una terza sezione che mappa il livello della ventola (fan level) all\u0026rsquo;intervallo di temperatura:\n$ cat /etc/thinkfan.conf sensors: - tpacpi: /proc/acpi/ibm/thermal # Alcune delle voci di temperatura in /proc/acpi/ibm/thermal potrebbero essere # irrilevanti o inutilizzate, quindi è possibile selezionare quelle singole: indices: [1, 2, 4, 5, 6] fans: - tpacpi: /proc/acpi/ibm/fan levels: - [0, 0, 60] - [2, 60, 65] - [3, 65, 70] - [5, 70, 75] - [6, 75, 80] - [7, 80, 85] - [\u0026#34;level disengaged\u0026#34;, 85, 255] Conclusione A seconda del vostro sistema, potete utilizzare molti altri programmi per controllare la velocità delle ventole in Linux; thinkfan ha il vantaggio di essere leggero e altamente configurabile.\n","permalink":"https://ilmanzo.github.io/it/post/quiet_thinkpad_fans/","summary":"\u003ch2 id=\"intro\"\u003eIntro\u003c/h2\u003e\n\u003cp\u003eIl portatile \u003ccode\u003eThinkpad P15\u003c/code\u003e è un\u0026rsquo;ottima macchina Linux, ma c\u0026rsquo;è un dettaglio fastidioso, come scrive la \u003ca href=\"https://wiki.archlinux.org/title/Lenovo_ThinkPad_P15_Gen_1\"\u003eWiki di Arch\u003c/a\u003e:\n\u003cem\u003e\u0026ldquo;Il funzionamento predefinito delle ventole è rumoroso, poiché sono praticamente sempre a media potenza. Il programma thinkfan può essere utilizzato per ottenere un funzionamento più silenzioso, pur mantenendo temperature ragionevoli.\u0026rdquo;\u003c/em\u003e . Rendiamolo più silenzioso.\u003c/p\u003e\n\u003ch2 id=\"prerequisiti\"\u003ePrerequisiti\u003c/h2\u003e\n\u003cp\u003eInstallare il pacchetto rpm \u003ccode\u003ethinkfan\u003c/code\u003e e abilitare il demone:\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cpre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;\"\u003e\u003ccode class=\"language-shell\" data-lang=\"shell\"\u003e\u003cspan style=\"display:flex;\"\u003e\u003cspan\u003e\u003cspan style=\"color:#75715e\"\u003e# zypper in thinkfan \u0026amp;\u0026amp; systemctl enable --now thinkfan\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/div\u003e\u003cp\u003eAssicurarsi che i moduli vengano caricati all\u0026rsquo;avvio con le opzioni per sovrascrivere il controllo delle ventole e abilitare le funzionalità sperimentali:\u003c/p\u003e","title":"Ventole silenziose su Thinkpad P15"},{"content":"Intro Lo unit testing di funzioni Bash comporta il processo di verifica sistematica della correttezza e dell\u0026rsquo;affidabilità delle singole funzioni all\u0026rsquo;interno di uno script Bash. Sebbene Bash sia utilizzato principalmente per lo scripting e l\u0026rsquo;automazione, è importante garantire che le funzioni all\u0026rsquo;interno dei tuoi script funzionino come previsto, specialmente quando gli script diventano più complessi. Lo unit testing in Bash può aiutare a individuare bug e prevenire comportamenti imprevisti.\nFixing bugs Mentre lavoravo alla correzione di un bug in uno script di shell interno, volevo aggiungere alcuni unit test per garantirne la correttezza. Dopo una rapida ricerca, ho trovato questo \u0026ldquo;framework\u0026rdquo; a file singolo (grazie, Ryan) che fornisce asserzioni in stile xUnit. Possiamo quindi usarlo come punto di partenza.\nIl problema principale dello script sotto test è che contiene funzioni che manipolano direttamente il filesystem dell\u0026rsquo;host, quindi può essere difficile estrarre e fare il mocking di queste interazioni per un test appropriato.\nHo deciso quindi di utilizzare un semplice container per eseguire lo script in un ambiente isolato. Già che ci siamo, non servono demoni, basta usare rootless podman. Questo è lo script principale, l\u0026rsquo;unico da eseguire e che avvia tutte le suite di test:\n#!/bin/bash # Questo script eseguirà gli unit test per le funzioni nel file \u0026#34;mylib\u0026#34;. # i test vengono eseguiti in un container per garantire l\u0026#39;isolamento dal sistema host if [ \u0026#34;$EUID\u0026#34; -eq 0 ] then echo \u0026#34;Please don\u0026#39;t run this script as root\u0026#34; exit fi # opzionalmente, puoi usare immagini di distribuzioni diverse qui podman run -v ..:/mnt registry.opensuse.org/opensuse/leap:latest bash /mnt/unit_tests/test_mylib.ut Eseguire i test senza danneggiare il sistema All\u0026rsquo;interno dello stesso test_mylib.ut, che non è eseguibile, ho aggiunto un altro controllo di sicurezza, in modo che l\u0026rsquo;utente sia consapevole che lo script di test può essere eseguito in sicurezza solo all\u0026rsquo;interno di un container:\n#!/bin/bash source \u0026#34;/mnt/unit_tests/bunit.shl\u0026#34; source \u0026#34;/mnt/mylib.sh\u0026#34; function testSetup() { [...] } function test_single() { [...] assertEquals 0 $? } function test_duplicate() { [...] local output=$( ... ) assertNull \u0026#34;$output\u0026#34; } ## controllo di sicurezza if [ \u0026#34;$container\u0026#34; != \u0026#34;podman\u0026#34; ]; then echo \u0026#34;ERROR: this is script is not intended to be run directly.\u0026#34; echo \u0026#34;Don\u0026#39;t run this script standalone/outside a container, it will break your system\u0026#34; exit 0 else echo \u0026#34;Starting test...\u0026#34; runUnitTests fi Outro In conclusione, lo unit testing delle funzioni Bash è una pratica essenziale per garantire l\u0026rsquo;affidabilità, la correttezza e la manutenibilità dei vostri script. Creando suite di test complete e impiegando framework di test, gli sviluppatori possono individuare precocemente i bug, migliorare la qualità del codice e apportare modifiche ai propri script con sicurezza. Sebbene il test degli script Bash possa richiedere considerazioni aggiuntive a causa delle loro interazioni con risorse esterne, i vantaggi dello unit testing superano di gran lunga le sfide, portando a soluzioni di scripting più robuste e prevedibili.\n","permalink":"https://ilmanzo.github.io/it/post/unit_testing_bash_functions/","summary":"\u003ch2 id=\"intro\"\u003eIntro\u003c/h2\u003e\n\u003cp\u003eLo unit testing di funzioni Bash comporta il processo di verifica sistematica della correttezza e dell\u0026rsquo;affidabilità delle singole funzioni all\u0026rsquo;interno di uno script Bash. Sebbene Bash sia utilizzato principalmente per lo scripting e l\u0026rsquo;automazione, è importante garantire che le funzioni all\u0026rsquo;interno dei tuoi script funzionino come previsto, specialmente quando gli script diventano più complessi. Lo unit testing in Bash può aiutare a individuare bug e prevenire comportamenti imprevisti.\u003c/p\u003e\n\u003ch2 id=\"fixing-bugs\"\u003eFixing bugs\u003c/h2\u003e\n\u003cp\u003eMentre lavoravo alla correzione di un bug in uno script di shell interno, volevo aggiungere alcuni unit test per garantirne la correttezza. Dopo una rapida ricerca, ho trovato questo \u003ca href=\"https://github.com/rafritts/bunit\"\u003e\u0026ldquo;framework\u0026rdquo; a file singolo\u003c/a\u003e (grazie, Ryan) che fornisce asserzioni in stile \u003cem\u003exUnit\u003c/em\u003e. Possiamo quindi usarlo come punto di partenza.\u003c/p\u003e","title":"Utilizzo dei container per lo unit testing di funzioni bash"}]