Ho condotto un esperimento di scrittura di software per analizzare i risultati di una esportazione delle mie conversazioni con ChatGPT. Ma ho deciso di aiutarmi nell’analisi con un Large Language Model che sicuramente sa fare molto bene questo lavoro e in più con un LLM in locale, per vedere come funziona.
Negli ultimi giorni ho avuto problemi intermittenti con un mouse Bluetooth Trust (modello 24059-02) connesso al mio HP con Ubuntu. Il mouse ha funzionato regolarmente per un paio di giorni, poi improvvisamente ha smesso di …
Ho installato la versione 24.3.1.347 di Oracle SQL Developer però ho una sorpresa: sembra non sia possibile raggruppare le connessioni in cartelle. Risposta: Eh sì — è una sorpresa che ha colpito parecchi utenti anche …
Quando si esegue uno script Bash ci sono due modalità principali da impiegare, che hanno conseguenze diverse sull’ambiente della shell. 1. Esecuzione diretta (./script.sh) Quindi può solo leggere le variabili di ambiente della shell madre …
Preso dalla curiosità di ripassare l’assembly (tanti anni fa studiai il Motorola 68000) ho scritto una guida ordinata che illustra passo per passo come scrivere un semplice programma Assembly in real-mode (16 bit), confezionarlo come …
AppArmor (“Application Armor”) è un modulo di security del kernel di Linux
Situazione: ho due schemi database MySQL. Attenzione: nello stesso server.
Nel primo importo i dati in una tabella da un file csv utilizzando l’istruzione LOAD DATA senza problemi.
Nel secondo voglio fare la stessa cosa, sullo stesso server, ma ottengo questo errore:
Error Code: 1290. The MySQL server is running with the --secure-file-priv option so it cannot execute this statement
Cioò che dice la documentazione di MySQL e ciò che trovo in tutti i forum è di modificare il file di configurazione
/etc/mysql/my.cnf
per aggiungere una riga che specifichi l’opzione secure-file-priv, per dichiarare qual è la directory preposta ai file da caricare, ma ci sono due problemi
l’opzione è già specificata e anche se metto i file in quella cartella, l’errore non scomapre; anche se cambio la cartella e riavvio il server (e posiziono i file csv in quest’ultima cartella), l’errore non scompare
il server è lo stesso
Il problema è l’applicazione apparmor di cui ho già parlato in un altro articolo. In sostanza la soluzione era far rileggere ad apparmor il suo file di configurazione (operazione che di tanto intanto si rende necesssaria per cui ho raccolto i comandi uno script Bash):
#!/bin/bash
apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld
service mysql stop
service mysql start
E tutto va a posto. Non mi spiego ancora, però, come fosse possibile eseguire il LOAD DATA per uno schema e non per un’altro.
Pillola di networking numero 2 sul funzionamento del routing.
Come funziona il routing?
Il routing (o instradamento) è il processo di inviare pacchetti IP da una rete ad un altra. Un router è un dispositivo che possiede almeno due schede di rete (NIC o Network Interface Controller), una fisicamente collegata da una rete, e l’altra collegata all’altra rete. Un router può collegare qualsiasi numero di reti, basta che abbia a bordo un egual numero di schede di rete.
Il routing è l’attività di scambio di pacchetti tra reti diverse
Strategie di routing per connettere più di due reti
Se si hanno due reti basta un router ed è estremamente semplice configurarlo. Quando però ci muoviamo verso reti più grandi, le cose si fanno un po’ più complicate. Per esempio, se volessimo collegare tre reti possiamo collegarle in due modi diversi: il primo è in daisy chain con due soli router. Un altro modo è collegarle direttamente usando tre router.
Routing tra più reti, diverse strategie
Nella configurazione 1, se il router A o B non funzionano più, nessuna macchina della sottorete A riuscirà più a comunicare con la sottorete C (oltre che con la rete B) perché c’è un unico percorso che li collega. Ma se aggiungiamo un altro router tra le sottoreti A e C (configurazione 2), avremo due percorsi tra A e C che rendono la rete pià efficiente.
I router non solo smistano il traffico vesro altre sottoreti, ma imparano anche qual è il percorso più veloce e lo usano per primo. Usando per esempio la configurazione 2 sopra , la sottorete A può connettersi alla sottorete C attraverso due percorsi: uno direttamente attraverso il router C (1 hop, cioè 1 salto) e uno attraverso i router A e B (2 salti). Quando si invia traffico dalla sottorete A alla sottorete C, vorremmo ovviamente andare direttamente passando per il router C. Questa è la rotta più veloce e più efficiente, ma come fa a sapero il router? Lo sa perché usa una informazione chiamata valore metrico, o metrica. Ogni rotta che il router conosce ha un valore di metrica ad essa assegnata. Una metrica è fondamentalmente un indice di preferenza. Se ci sono due strade per la stessa destinazione, allora quella con metrica più bassa verrà ritenuta la più efficiente. I router usano sempre questa rotta finché non fallisce, nel qual caso useranno la rotta con il prossimo indice metrico più basso e così via. I router conservano tutte queste informazioni in una tabella di routing di cui ci occuperemo nella Puntata 3 – La tabella di routing.
Questo di installare PHP e Oracle è un compito piuttosto ingrato perché ogni volta trovo delle novità. Come regola aurea non utiizzo PECL (PHP Extensions Communiti Library) perché qualcosa va regolarmente storto. Scarico i sorgenti della Oracle Call Interface (oci) e compilo a mano: funziona e si fa prima
$ mkdir ~/Scaricati/php/ & cd ~/Scaricati/php/
$ wget https://pecl.php.net/get/oci8-2.2.0.tgz
$ tar -xzvf oci8–2.2.0.tgz
$ cd oci8–2.2.0
$ phpize
$ ./configure — with-oci8=instantclient,/usr/lib/oracle/12.2/client64/lib
...
$ make
$ sudo make install
Per non modificare altri parametri di configurazione, sostituisco il file oggetto appena ottenuto al posto di quello di riferimento
Ora configuriamo Apache perché carichi questa libreria: non serve (come scritto in altri tutorial) modificare i file /etc/php/7.4/*/php.ini, ma è sufficiente aggiungere il modulo nella directory /etc/php/7.4/mods-available:
$ less /etc/php/7.4/mods-available:/oci8.ini
; configuration for php OCI8 module
; priority=20
extension=/usr/lib/php/20190902/oci8.so
Ultimo passaggio: modificare le variabili di ambiente di Apache perché ritrovi le librerie:
Per vedere dove sono installate le estensioni (i codici oggetto .so che realizzano i plugin come i connettori a database, l’uso delle funzioni cURL etc):
Ho però un problema, l’installazione della libreria curl mi ha provocato una serie di errori che vengono stapati ad ogni invocazione dell’interprete php client o ad ogni riavvio di Apache:
root@jsbach[11:26:44]:apache2$ php -v
PHP Warning: PHP Startup: Unable to load dynamic library 'curl' (tried: /usr/lib/php/20190902/curl (/usr/lib/php/20190902/curl: cannot open shared object file: No such file or directory),
/usr/lib/php/20190902/curl.so (/usr/lib/php/20190902/curl.so: symbol curl_mime_addpart version CURL_OPENSSL_4 not defined in file libcurl.so.4 with link time reference)) in Unknown on line 0
PHP 7.4.2 (cli) (built: Feb 1 2020 17:49:29) ( NTS )
root@jsbach[11:25:47]:apache2$ sudo ldd /usr/lib/php/20190902/curl.so
/usr/lib/php/20190902/curl.so: /usr/local/lib/libcurl.so.4: no version information available (required by /usr/lib/php/20190902/curl.so)
linux-vdso.so.1 (0x00007fffea95f000)<br>
libcurl.so.4 => /usr/local/lib/libcurl.so.4 (0x00007f5924bd3000)
Come evidenziato dal comando esiste un conflitto tra la versione di libcurl installata in passato (/usr/local/lib/libcurl.so.4) e quella nuova (/usr/lib/php/20190902/curl.so); decido di rinominare quella vecchia:
Ho dovuto fabbricarmi un allarme visivo per una attività che devo svolgere a intervalli regolari durante la giornata.
Ho realizzato questo compito utilizzando
Gnome
cron
Gnome
Gnome open source free software linux desktop environment
Gnome è uno degli ambienti grafici desktop utiizzati da molte distribuzioni Linux (RedHat, Ubuntu, Fedora e altri) per gestire il sistema grafico a finestre. Esso si basa sul server X (come gli altri sistemi, ad esempio KDE). Non ho sviluppato molto in questo ambiente tipicamente desktop, essendomi occupato più di web.
In ogni caso lo trovo molto divertente; in pratica si programma in Javascript e si usa uno speciale interprete che utilizza le API del server, gjs.
Ho preso spunto dal questo tutorial (Gnome developer) ed l’ho personalizzato. In sostanza si tratta di aprire una finestra di X da linea di comando: essa contiene una semplice immagine e un messaggio, l’aspetto è questo:
Finestra che si apre eseguendo il programma Gnome listato qui sotto
Il codice Javascript è questo di seguito, lo descrivo sommariamente, per i dettagli si veda il tutorial di Gnome:
#!/usr/bin/gjs
imports.gi.versions.Gtk = '3.0';
const Gtk = imports.gi.Gtk;
class MyGrid {
// Crea l'applicazione
constructor() {
this.application = new Gtk.Application();
// Collega i segnali 'activate' e 'startup' alle funzioni di callback
this.application.connect('activate', this._onActivate.bind(this));
this.application.connect('startup', this._onStartup.bind(this));
}
// La funzione di callback per il segnale 'activate' presenta la finestra quando attiva
_onActivate() {
this._window.present();
}
// La funzione di callback per il segnale 'startup' costruisce la UI
_onStartup() {
this._buildUI ();
}
// Costruisci la UI dell'applicazione
_buildUI() {
// Crea la finestra applicativa
this._window = new Gtk.ApplicationWindow({
application: this.application,
window_position: Gtk.WindowPosition.CENTER,
border_width: 10,
title: "Ora di bere!"});
// Crea una immagine
this.path = '/home/marcob/gnome';
this._image = new Gtk.Image ({ file: this.path + '/bottiglia.png' });
// Crea una etichetta
this._label = new Gtk.Label ({ label: "Questo per ricordarti di bere!" });
// Crea la griglia
this._grid = new Gtk.Grid ();
// Posiziona immagine ed etichetta nella griglia
this._grid.attach (this._image, 0, 0, 1, 1);
this._grid.attach (this._label, 0, 1, 1, 1);
// Aggiungi la griglia alla finestra
this._window.add (this._grid);
// Mostra la finestra e tutti gli oggetti figli
this._window.show_all();
}
};
// Lancia l'applicazione
let app = new MyGrid ();
app.application.run (ARGV);
Leggiamo brevemente il sorgente: con il preambolo importiamo le funzioni dello Gnome Toolkit (GTK+) e definiamo la classe MyGrid che ci consente di progettare la finestra come una griglia.
Si collegano (bind) gli eventi a dei metodi che definiamo subito dopo (present() e _buildUI()).
Il core del programma è il metodo this._buildUI() che costruisce la User Interface utilizzano i metodi della libreria GTK+:
si definisce una nuova finestra GTK: new Gtk.ApplicationWindow();
si definiscono varie proprietà come posizione e titolo;
si definiscono una immagine caricata da file system e un titolo della finestra più altre proprietà geometriche;
si definisce la griglia all’interno della quale collocare gli oggetti (new Gtk.Grid ());
e si posizionano gli oggetti (this._grid.attach());
Finalmente si istanzia questa nuova classe e si lancia il metodo application.run();
Se si vogliono maggiori dettagli si veda il tutorial di Gnome
Ora questo programmino si può semplicemente lanciare da shell
$ gjs native.js
Il passo successivo è eseguirlo in automatico con periodicità (ad esempio ogni 20 minuti). Per fare questo utilizzo cron.
Modifico il mio cron personale tramite il comando
$ crontab -e
Il contenuto del file è il seguente (mi limito alla riga che definisce il comando):
#
# m h dom mon dow command
0,20,40 /usr/bin/gjs /home/marcob/gnome/native.js
Questa impostazione provoca il ripetersi del comando ogni venti minuti.
Tuttavia incappo in un problema: in realtà il programma non parte e viene generato un errore nel syslog
Unable to init server: Impossibile connettersi: Connessione rifiutata
(gjs:25833): Gtk-WARNING **: 16:00:01.480: cannot open display:
Questo è perché il processo cron esegue i comandi in uno spazio “virtuale” che non ha display. Quindi occorre indicare a cron quale display usare:
#
# m h dom mon dow command
0,20,40 export DISPLAY=:0 && /usr/bin/gjs /home/marcob/gnome/native.js
L’indicazione della direttiva export DISPLAY=:0 serve per poter agganciare il server X. Il risultato è quello voluto ed io sono forzato a bere un po’ d’acqua ogni venti minuti per evitare la ritenzione idrica! 🙂
Problemi con il caricamento delle pagine web, fastidiosi reflow, scarsa responsività? Arriva AMP (Accelerated Mobile Pages): un framework HTML open source sviluppato in Google che fornisce una modalità semplice di creazione di pagine web che sono veloci, dal caricamento fluido e che danno priorità all’UX prima di tutto, primariamente per i dispositivi mobile.
Se avete notato, vedrete che quando effettuate una ricerca su Google con un dispositivo mobile come lo smartphone, quando cliccate sul link del risultato che volete visitare, può accadere che non compaia subito la pagina target ma venga invocato il sito ampproject.org in modo tale da rendere i risultato in modo veloce e fluido. Questo dipendentemente dal fatto che il sito sia stato attrezzato per utilizzare questo framework per ottimizzare le pagine con AMP.
AMP definisce una serie di tags estesi (che si chiamano componenti) che vengono gestiti da una libreria JavaScript obbligatoria che ogni pagina AMP deve caricare all’inizio della sezione <head> che hanno la proprietà di caricarsi velocemente nel browser e completamente prima di venire visualizzate, fornendo un’ottima esperienza utente (UX).
Per esempio, uno dei problemi che molti siti web mostrano (soprattutto se non ottimizzati per viewport del tipo smartphone) è il caricamento progressivo delle risorse della pagina web e il passaggio attraverso una fase in cui la pagina si ridisegna in continuazione: viene caricato un blocco di testo, poi un’immagine e viene ricalcolato il blocco di testo per aderire al layout… questo lavorio intermedio, piuttosto fastidioso, è detto HTML reflow e AMP fornisce degli strumenti che consentono di visualizzare la pagina solamente al termine delle determinazioni della geometria da parte del motore grafico del browser e del caricamento di tutte le risorse (font, testo, immagini).
Tecnicamente si tratta di ridefinire un set di pagine web dalla estensione .amp.html che utilizzano i componenti amp anziché i tag html standard (per esempio, l’utilizzo di <amp-img> anziché del tag <img>). Il rendering di questi tag è realizzato con la libreria Javascript che si trova nel CDN di ampproject.org.
Ma la potenza di AMP non si ferma ai siti web: può essere applicata in modo verticale a blog, stories, siti di ecommerce, ads e addirittura email!
Scrivendo gli ultimi articoli ho pensato di chiarire la differenza tra linguaggi di scripting e di programmazione. Il criterio di differenza più netto che ho trovato in rete è quello di suddividere in queste due categorie i linguaggi di programmazione allo stesso modo in cui si suddividono i linguaggi tra compilati ed interpretati.
Ma ho trovato parecchie inesattezze su quest’ultima classificazione, per una buona parte dei linguaggi attualmente disponibili.
La verità è che la differenza non è più così netta per tutta una serie di linguaggi molto popolari.
PHP non è un linguaggio interpretato come scritto da più parti, ma ha il suo bel bytecode generato a runtime da un compilatore che fa parte del core Zend. In pratica il compilatore Zend compila il sorgente in bytecode che viene poi interpretato dalla macchina virtuale Zend. Quindi non è veramente del tutto un linguaggio interpretato né del tutto compilato come si può dire del C.
Una risorsa che riporta un chiaro diagramma sul funzionamento di PHP è disponibile qui.
Anche Java viene trasformato in bytecode ed eseguito da una macchina virtuale (Oracle JVM, IcedTea etc).
La stessa cosa si può dire per Python in quanto anche qui ho una macchina virtuale che esegue bytecode (i file .pyc che vengono creati a runtime).
Quindi il codice che scriviamo non viene tradotto ed eseguito instantaneamente come succedeva per il BASIC, bensì viene compilato in bytecode e il bytecode viene interpretato.
C’è una importante differenza tra interpretazine dei bytecode e i programmi compilati puri come il C: il binding delle variabili, ossia il calcolo dell’indirizzo delle variabili, non è eseguito alla compilazione (anzi in realtà al momento dell’esecuzione del linker) ma all’esecuzione della virtual machine, quindi runtime. Nel C invece viene calcolata la posizione di memoria delle variabili in modo relativo nel codice hello.o e poi il linker le rende assolute nel contesto esecutivo del sistema operativo.
Javascript è un linguaggio di scripting poiché viene eseguito dal browser che diventa una sorta di “sistema operativo”. Ma anche questa nozione è superata. Il browser come Chrome in realtà opera una traduzione in bytecode, tramite il motore (engine, un altro modo di chiamare l’interprete) V8, degli script collegati alla pagina web. V8 è l’evoluzione dell’interprete JavaScript dei primi browser in un compilatore bytecode allo stesso modo di Java, PHP e Python. V8 è usato anche da node.js.
Un altro criterio distintivo potrebbe essere l’interazione con l’utente. Generalmente gli script consistono in una serie di comandi automatizzati che implementano la logica di un programma inteso come macchina di Von Neumann (inizializzazione di variabili, decisioni, cicli) e che vengono eseguiti in modalità batch, una modalità in cui il programma parte (spesso da solo, su innesco di uno scheduler come cron), fa una serie di operazioni e il risultato può essere verificato da un operatore a distanza di tempo e anche senza interazione alcuna con il software (ad esempio andando a vedere i log o i risultati in un database). In questa categoria si inseriscono gli script bash, potentissimi programmi (o script?) Unix/Linux.
Ho due applicazioni residenti in due cartelle diverse che condividono uno stesso file (ad esempio il file contiene una stessa istruzione sql che viene emendata).
È chiaro che in partenza una situazione del genere non è nemmeno consigliabile. Però questa è la condizione del software del mio cliente e per non dover fare una profonda ristrutturazione devo metterci una… “patch”. Chiedo venia.
A parte gli scherzi, devo apportare la stessa modifica a due file uguali in due applicazioni diverse.
Estraggo la patch applicata al primo repository
$ git diff [ID COMMIT1] [ID COMMIT2] > diff.patch
Sposto il file ottenuto che contiene la patcj da applicare (diff.patch) nella cartella del secondo repository
$ mv diff.patch [altraCartella]
Applico la patch al secondo repository Git
$ git apply diff.patch
Se la patch è applicabile Git lo farà.
Se i file sono diversi non lo farà.
Infatti il contenuto della patch è
marcob@jsbach[15:29:18]:altraCartella$ cat diff.patch
diff --git a/inviti/inv610.db.inc.php b/inviti/inv610.db.inc.php
index 9d9b678..797f68d 100644
--- a/inviti/inv610.db.inc.php
+++ b/inviti/inv610.db.inc.php
@@ -272,7 +272,7 @@ where
and d.id_tipo = f.id
- and f.cod in (1, 2, 4, 5)
+ and f.cod in (1, 2, 3, 4, 5)
Git controlla prima che l’hash del secondo file (di arrivo) sia uguale al file da emendare: se sono diversi, vuol dire che non posso applicare la patch e Git terminerà.
Lavorando coi layout di Groovy on Grails (le cosiddette pagine GSP: Groovy Server Pages) un web designer può efficientemente creare una struttura di inclusione che fattorizza in modo ottimale le parti di html. Cioè può raccogliere a fattore comune tutte le parti di layout che sono comuni a tutte le funzionalità legate al singolo controller e personalizzare le parti dedicate al singolo metodo.
Usiamo la prassi “usa una Convenzione piuttosto che Configurare” (convention over configuration). Questo frammento di progetto è l’esposizione casuale di una citazione (quote) tratta da una tabella, assieme al suo autore.
il layout si chiamerà come il controller: se abbiamo definito un controller QuoteController troveremo un template nei layouts con lo stesso nome quote (utilizzando il programma create-controller Grails creerà già da solo un controller e un layout per default):
grails-app/views/layouts/quote.gsp
All’interno della view dedicata alla citazione poi dovremo convenire di chiamare il blocco div che vogliamo personalizzare con il nome del layout:
Utilizziamo tecnologie come i cookie per memorizzare e/o accedere alle informazioni del dispositivo. Lo facciamo per migliorare l'esperienza di navigazione e per mostrare annunci personalizzati. Il consenso a queste tecnologie ci consentirà di elaborare dati quali il comportamento di navigazione o gli ID univoci su questo sito. Il mancato consenso o la revoca del consenso possono influire negativamente su alcune caratteristiche e funzioni.
Funzionale
Sempre attivo
L'archiviazione tecnica o l'accesso sono strettamente necessari al fine legittimo di consentire l'uso di un servizio specifico esplicitamente richiesto dall'abbonato o dall'utente, o al solo scopo di effettuare la trasmissione di una comunicazione su una rete di comunicazione elettronica.
Preferenze
L'archiviazione tecnica o l'accesso sono necessari per lo scopo legittimo di memorizzare le preferenze che non sono richieste dall'abbonato o dall'utente.
Statistiche
L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici.L'archiviazione tecnica o l'accesso che viene utilizzato esclusivamente per scopi statistici anonimi. Senza un mandato di comparizione, una conformità volontaria da parte del vostro Fornitore di Servizi Internet, o ulteriori registrazioni da parte di terzi, le informazioni memorizzate o recuperate per questo scopo da sole non possono di solito essere utilizzate per l'identificazione.
Marketing
L'archiviazione tecnica o l'accesso sono necessari per creare profili di utenti per inviare pubblicità, o per tracciare l'utente su un sito web o su diversi siti web per scopi di marketing simili.
Commenti recenti