Pillole MySQL – come l’utility apparmor può darci problemi

AppArmor ("Application Armor") è un modulo di security del kernel di Linux
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.

Stay tuned.

Appunti di TCP/IP – Puntata 2: routing

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.

Riferimenti

Installare PHP e Oracle

php e oracle
php e oracle

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

$ sudo mv /usr/lib/php/20190902/oci8.so oci8.so.old
$ sudo cp modules/oci8.so /usr/lib/php/20190902/

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:

$ sudo nano /etc/apache2/envvars
...
[Aggiungere]
export PATH=”/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/home/marcob/Scaricati/php/oci8-2.2.0/modules:/usr/lib/oracle/12.2/client64:/usr/lib/oracle/12.2/client64/lib
export LD_LIBRARY_PATH=/usr/lib/oracle/12.2/client64/lib
#export ORACLE_HOME=/usr/lib/oracle/12.2/client64

A questo punto riavvio il servizio

$ sudo service apache2 restart
$

E provo a far girare lo script che doveva leggere SYSDATE da una istanza Oracle e stamparlo su una pagina web:

SQL = select sysdate from dual
PRM = marcob/marcob@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=orahost.myserver.it)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=oradb)(SERVER=DEDICATED)))
Oracle correctly connected!

/var/www/html/oracle/index.php:8:
array (size=1)
  0 => 
    array (size=1)
      'SYSDATE' => string '06-FEB-20' (length=9)

Per fare un po’ di sintesi, tre sono le directory importanti per il funzionamento di questa estensione:

PathFile
/usr/lib/oracle/12.2/client64/lib Instant client nativi Oracle
/usr/lib/php/20190902/libreria Oracle client compilata
/etc/php/7.4/configurazione per agganciare il client ai comandi (riga di comando, Apache, Fast CGI)

E anch’io termino con un bel

Fine!

Ulteriori letture

Pillole PHP: gestire le estensioni

PHP: gestire le estensioni
PHP: gestire le estensioni

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):

marcob@jsbach[12:15:08]:php$ php-config --extension-dir
/usr/lib/php/20190902

Per abilitare il modulo curl per la versione 7.4

$ phpenmod -v 7.4 curl

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 )

Seguo questo howto su Github

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:

root@jsbach[11:34:45]:apache2# cd /usr/local/lib/
root@jsbach[11:35:58]:lib# ls -l | grep 'curl'
-rw-r--r-- 1 root root  1006444 mag 23  2017 libcurl.a
-rwxr-xr-x 1 root root     1042 mag 23  2017 libcurl.la
lrwxrwxrwx 1 root root       16 mag 23  2017 libcurl.so -> libcurl.so.4.4.0
lrwxrwxrwx 1 root root       16 mag 23  2017 libcurl.so.4 -> libcurl.so.4.4.0
-rwxr-xr-x 1 root root   542272 mag 23  2017 libcurl.so.4.4.0
root@jsbach[11:36:04]:lib# mv libcurl.so.4.4.0 libcurl.so.4.4.0.OLD
root@jsbach[11:38:53]:lib# ldd /usr/lib/php/20190902/curl.so
	linux-vdso.so.1 (0x00007fff9610f000)
	libcurl.so.4 => /usr/lib/x86_64-linux-gnu/libcurl.so.4 (0x00007f8d92a92000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8d926a1000)

Adesso il comando ldd (List Dynamic Library, che ci dice quali sono le librerie collegate ad un executable) non mostra più l’errore

root@jsbach[11:38:53]:lib# ldd /usr/lib/php/20190902/curl.so
	linux-vdso.so.1 (0x00007fff9610f000)
	libcurl.so.4 => /usr/lib/x86_64-linux-gnu/libcurl.so.4 (0x00007f8d92a92000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8d926a1000)
...

Altro problema. Eseguento il comando php -m (o qualsiasi comando che usa l’interprete php client) ho un Warning:

root@jsbach[11:55:43]:lib# php -m
PHP Warning:  Module 'curl' already loaded in Unknown on line 0
[PHP Modules]
calendar
Core
ctype
curl
date

Il problema è che carico due volte la libreria, una specificando nominalmente il modulo curl nelle extensions

root@jsbach[12:10:32]:cli# pwd
/etc/php/7.4/cli
root@jsbach[12:10:34]:cli# cat php.ini | grep curl
extension=curl
[curl]
;curl.cainfo =

e una a causa della presenza del file singolo di load nelle configurazioni:

root@jsbach[12:11:00]:cli# ll conf.d/ | grep curl
lrwxrwxrwx 1 root root   36 feb  4 16:23 20-curl.ini -> /etc/php/7.4/mods-available/curl.ini

Commento la riga extensions=curl del file /etc/php/7.4/cli/php.ini. Il comando ora da un ouput pulito:

root@jsbach[12:11:45]:cli# php -m
[PHP Modules]
calendar
Core
ctype
curl
date
...

Anche il riavvio di Apache ora è pulito:

marcob@jsbach[12:15:23]:php$ sudo service apache2 restart
marcob@jsbach[12:24:48]:php$ 

Inoltre ora phpinfo() mostra il modulo correttamente caricato:

Modulo cURL correttamente caricato

Pillole di GNOME/GTK

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 spftware linux desktop environment
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! 🙂

Pillole Python/Linux: pretty printing json files

Esaminando il contenuto di un file json con il comando less, mi veniva tutto su una linea

[{"id":"5d070cce-a504-4375-94c3-403f71eb4212","name":"prod36.mirth.mysite.it:8443","address":"https://prod36.mirth.mysite.it:8443","javaHome":"BUNDLED","heapSize":"512m","icon":"egyptian_pyramid.png","showJavaConsole":false,"sslProtocolsCustom":false,"sslProtocols":"","sslCipherSuitesCustom":false,"sslCipherSuites":"","useLegacyDHSettings":false},{"id":"7431b71c-7a2d-4be6-a7f1-6f1129e3a8ce","name":"https://test36.mirth.mysite.it:8443","address":"https://test36.mirth.mysite.it:8443","javaHome":"BUNDLED","heapSize":"512m","icon":"pepper.png","showJavaConsole":false,"sslProtocolsCustom":false,"sslProtocols":"","sslCipherSuitesCustom":false,"sslCipherSuites":"","useLegacyDHSettings":false}]

Piuttosto scomodo. Avrei potuto aprirlo con un editor con filtri di interpretazione, ma ho trovato questo metodo comodissimo:

$ less connections.json | python -m json.tool 

Il risultato è molto più confortevole

[
    {
        "address": "https://prod36.mirth.mysite.it:8443",
        "heapSize": "512m",
        "icon": "egyptian_pyramid.png",
        "id": "5d070cce-a504-4375-94c3-403f71eb4212",
        "javaHome": "BUNDLED",
        "name": "prod36.mirth.sanita.padova.it:8443",
        "showJavaConsole": false,
        "sslCipherSuites": "",
        "sslCipherSuitesCustom": false,
        "sslProtocols": "",
        "sslProtocolsCustom": false,
        "useLegacyDHSettings": false
    },
    {
        "address": "https://test36.mirth.mysite.it:8443",
        "heapSize": "512m",
        "icon": "pepper.png",
        "id": "7431b71c-7a2d-4be6-a7f1-6f1129e3a8ce",
        "javaHome": "BUNDLED",
        "name": "https://test36.mirth.sanita.padova.it:8443",
        "showJavaConsole": false,
        "sslCipherSuites": "",
        "sslCipherSuitesCustom": false,
        "sslProtocols": "",
        "sslProtocolsCustom": false,
        "useLegacyDHSettings": false
    }
]

AMP: HTML to its full potential

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!

Fonti: Search Engine Land, AMP Project, AMP dev

Qual è la differenza tra un programma e uno script?

script
script

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.

Riferimenti web

Pillole Git: come applicare una patch

git
git

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à.

Questa volta mi è andata di lusso.

Riferimenti

GSP: meccanismo di inclusione nei layout di Groovy

Layout di Groovy
Layout di Groovy

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:
grails-app/views/quote/random.gsp

in questo modo

<div id="quote">
    <q>${content}</q>

    <p>${author}</p>
</div>
  • A sua volta il nome della view è il nome del metodo del controller che viene invocato dalla url:
http://localhost:8080/qotd/quote/random

Memorizziamo che

  • controller -> layout e
  • metodo -> view e,
  • all’interno della view, div -> controller

Link utili su layout di Groovy