Quando si analizza il piano di esecuzione di una query Oracle, una delle prime cose che tende a far scattare un campanello d’allarme è questa: La reazione istintiva è spesso: «Sta facendo un Full Table …
Basta un prompt > per far venire voglia di scrivere un programma. Oggi ho provato StrayBASIC di Paolo Caressa sul mio computer con Ubuntu. È stata una sorpresa vedere Caressa in veste di guru informatico …
Configurare ssh-agent su CentOS permette di utilizzare Git tramite SSH senza dover inserire la passphrase della chiave privata a ogni pull o push, mantenendo comunque la protezione offerta dalla passphrase. Ovviamente tutto ciò che vi …
Devo confrontare due file CSV che forse contengono gli stessi dati: nei due file le righe sono ordinate diversamente. Voglio farlo son un solo comando da bash. Bash è un interprete di comandi (shell) che …
Nei giorni scorsi l’Agenzia per la Cybersicurezza Nazionale (ACN) ha pubblicato una segnalazione relativa ad alcune vulnerabilità individuate in n8n, la piattaforma open source per l’automazione dei workflow che utilizzo in un’installazione locale tramite Docker. …
Cosa fare quando si verifica l’errore Call to undefined function Ixudra\Curl\curl_init()?
cURL è una nota libreria per trattare gli url e gestire chiamate http.
Utilizzo una libreria per Laravel che mi permette di fare delle richieste HTTP da server a sever che si chiama ixudra/curl.
La libreria funziona a meraviglia, se non che, per un problema di configurazione del mio SO, ogni volta che aggiorno la libreria, cURL smette di funzionare.
Il sintomo (undefined function Ixudra \ Curl\Curl_init())
Il sintomo del problema che Laravel esibisce è questo
Symfony\Component\Debug\Exception\FatalThrowableError Call to undefined function Ixudra\Curl\curl_init()
In realtà il problema risiede ancora più in basso poiché nemmeno il client PHP è immune dagli effetti di questa errata configurazione; invocando semplicemente il client da riga di comando ho:
$ php -v
PHP Warning: PHP Startup: Unable to load dynamic library 'curl.so' (tried: /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), /usr/lib/php/20190902/curl.so.so (/usr/lib/php/20190902/curl.so.so: cannot open shared object file: No such file or directory)) in Unknown on line 0
PHP 7.4.6 (cli) (built: May 14 2020 10:02:44) ( NTS )
...
Il problema
La radice del problema si trova nella directory /usr/local/lib
La “sconfigurazione” è dovuta alla presenza del link simbolico libcurl.so.4, la soluzione proposta è quella di interromperlo perché prenda il sopravvento la versione di sistema. Il fatto è che ogni volta che viene eseguito un aggiornamento di cURL, viene ripristinato anche questo link simbolico reintroducendo l’errore.
Una soluzione
Nell’attesa di escogitare una soluzione più definitiva ho seguito il consiglio che si dava nel forum:
$ sudo mv libcurl.so.4.4.0 libcurl.so.4.4.0.OLD
Così facendo non ho più il problema al client:
$ php -v
PHP 7.4.6 (cli) (built: May 14 2020 10:02:44) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
with Zend OPcache v7.4.6, Copyright (c), by Zend Technologies
with Xdebug v2.9.5, Copyright (c) 2002-2020, by Derick Rethans
Come si vede, l’errore è scomparso. Affinche anche Apache possa beneficiare della soluzione del problema occorre riavviarlo:
$ sudo service apache2 restart
A questo punto anche l’applicazione Laravel torna a funzionare.
Lanciare in cron un metodo di un controller Laravel
Ho bisogno di eseguire un determinato metodo di un controller di un’applicazione sviluppata in Laravel regolarmente una volta al giorno.
Avevo fatto un programma bash e l’avevo inserito nella crontab, ci dovevo fare un aggiornamento quotidiano del mio DB da una sorgente dati.
Questo database mi serve per una applicazione live, per cui devo rendermi autonomo da operazioni manuali.
Avrei potuto replicare la modalità anche in remoto, ma ho preferito farlo attraverso Laravel, tirando dentro all’applicazione tutta la logica eseguita nello script di shell.
Un primo livello è stato quello di scrivere dei programmi PHP che leggevano la sorgente e aggiornavano il db attraverso un nuovo metodo dei miei controller dedicati a quelle entità relazionali.
Quindi potevo lanciare manualmente invocando un URL la funzionalità e tutto filava meravigliosamente.
L’ultimo passo è rendere autonoma l’applicazione Laravel perché ad una certa ora del giorno esegua il task. Qui arriva l’utilissimo Scheduler di Laravel.
L’idea di fondo è quella di invocare in modalità cronla rotta URI che scatena il metodo del controller. Questa funzionalità di Laravel si basa comunque sul clock del sistema operativo: un cron batch che viene lanciato ogni minuto e che viene preso come riferimento da Laravel per temporizzazioni diverse. Quindi è comunque obbligatorio agire su due fronti:
sullo scheduler di Laravel e
sul crontab di sistema.
Vediamo come si fa.
Estendere Laravel/Artisan creando un comando che chiama una rotta
Premetto che sto utilizzando questa versione di Laravel:
$ php artisan --version
Laravel Framework 6.18.8
Possiamo estendere le funzionalità di artisan che usiamo correntemente come ad esempio:
$ php artisan make:controller InvoiceController
per fare in modo che si possa invocare un nostro comando artisan; quindi la prima cosa da fare è creare un nuovo comando artisan che chiamo route:call. Ho bisogno inoltre che a questo comando venga affiancata una serie di parametri opzionali.
Esistono già dei comandi artisan per gestire le rotte; se provo a lanciare il mio comando ancora da definire ho questa risposta:
$ php artisan route:call
Command "route:call" is not defined.
Did you mean one of these?
route:cache
route:clear
route:list
Per creare un nuovo comando artisan si fa così:
$ php artisan make:command RouteCall
Laravel creerà un nuovo comando che consiste in una classe che si chiamerà come il primo parametro (RouteCall) e la sintassi (signature) sarà definita all’interno del file contenente la nuova classe, che viene creata dentro la cartella app/Console/Commands ed estende la classe Command:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Request;
class RouteCall extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'route:call {--uri=}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Call route from CLI';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$request = Request::create($this->option('uri'), 'GET');
$this->info(app()['Illuminate\Contracts\Http\Kernel']->handle($request));
}
protected function getOptions()
{
return [
['uri', null, InputOption::VALUE_REQUIRED, 'The path of the route to be called', null],
];
}
}
In particolare osserviamo la sintassi del comando contenuta nella proprietà $signature che è quella che abbiamo definito nel creare la class nel comando sopra a cui però ho aggiunto il parametro {--uri} che è l’URI del metodo da chiamare. All’interno della classe interessata aggiungerò anche una gestione del parametro in modo tale da renderlo opzionale: in pratica il parametro è una data in formato YYYY-MM-DD: se non la passo nell’URI, sottointendo data odierna.
Il metodo chiave è handle() che trasforma un comando Linux in una request HTTP. Come si vedrà adesso il controllo passa al modulo Kernel.php.
Schedulazione del comando
Il secondo passaggio è quello di definire la temporizzazione del comando:
<?php
// file app/Console/Kernel.php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
'App\Console\Commands\CallRoute',
];
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('call:route')->dailyAt('19:00');
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
require base_path('routes/console.php');
}
}
Il nodo centrale è il metodo schedule() che contiene uno o più comandi, ognuno con la temporizzazione desiderata, in questo caso voglio che il Kernel dello scheduler avvii il comando route:call ogni giorno alle 19:00.
Avvio dello scheduler
La terza operazione è quella di creare una nuova voce nella crontab che avvia lo scheduler:
$ crontab -e
...
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h dom mon dow command
* * * * * cd path/to/project/ && php artisan schedule:run >> /dev/null 2>&1
questo job è chiamato con la risoluzione massimo di 1 volta al minuto, poi la logica di Laravel farà in modo che i singolo job vengano eseguiti su temporizzazioni diverse – come sono state definite nel metodo Kernel->schedule()– ma sempre con il clock di riferimento dettato dalla crontab.
Quindi riassumendo, a partire dalla causa primaria, fino all’effetto ultimo:
crontab invoca lo scheduler di Laravel ogni minuto;
lo scheduler controlla se c’è quacosa da eseguire in base alle diretive everyMinute(), dailyAt(), daily() eccetera definite per i comandi invocati nel metodo schedule() del Kernel;
il Kernel avvia tutti i comandi registrati nel metodo schedule();
ognuno di questi metodi è invocato con un comando artisan(es. route:call) ma in generale potrebbe anche essere una chamata a Eloquent per scrivere qualcosa nel database;
il singolo comando artisan è definito in una classe specifica (es. RouteCall) contenuta nella directory app/Console/Commands/
il metodo che esegue quanto si desidera (nel mio caso la trasformazione di un comando shell come php artisan route:call in una request HTTP) è il metodo handle()
Modificare una singola tabella database con Laravel
Al volte ci si trova a dover ridisegnare una tabella database (ad esempio per dover modificare il tipo dei campi o la dimensione, o aggiungere / togliere campi) in fase di progetto, o ancora più spesso in fase di prototipazione.
E spesso non è nemmeno richiesto di conservare una storia di queste modifiche perché semplicemente si sta modellando una versione finale non ancora soddisfacente.
Quindi invece di procedere con modifiche puntuali sul campo, ad esempio con un equivalente di
ALTER TABLE RENAME COLUMN myField AS Field1;
è più immediato fare un drop e ricreare la tabella, eventualmente salvando i dati o preparando uno script di alimentazione da lanciare all’occorrenza.
Sebbene si possano effettuare entrambe le operazioni (più soft con l’alter table o più drastica con il drop/create) vediamo come si fa a “droppare” e ricreare la tabella con i comandi di migrazione di Laravel.
Lo script di creazione tabella contiene già per default i due metodi per costruire e distruggere la tabella, rispettivamente up() e donw():
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateDatiRegioniTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('dati_regioni', function (Blueprint $table) {
$table->bigIncrements('id');
...
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('dati_regioni');
}
}
Il metodo up() viene invocato quando si esegue la migrazione:
$ php artisan migrate
ed il file ad essere eseguito è l’ultimo (il più recente) che si trova nella directory database/migrations. Per droppare la tabella si esegue:
$ php artisan migrate:rollback
e successivamente, per ricrearla:
$ php artisan migrate
Per eseguire operazioni su una determinata tabella, individuare il file contente le migrazioni per quella tabella e modificarne il nome in modo tale che la stringa sia in senso lessicografico la più grande di tutte
Deploy with git: ovvero, come usare Git come strumento per gestire i deploy oltre che per versionare il codice. Questo articolo molto vecchio l’ho trovato originariamente nel blog di Kris Jordan ma mi è tornato utile adesso. Lo accomodo per il caso di mio interesse.
Ho un progetto in un host locale, un ambiente di produzione e un ambiente di staging al quale vorrei applicare in tempo reale le modifiche appena consegnate (commit) alla versione di sviluppo.
Questo aticolo, per arrivare a questo obiettivo, propone di creare un repository “bare” in remoto che serve solo per fare da tramite tra cartella di sviluppo in localhost e la cartella di deploy/stage in remoto, come illustrato nella figura 1
Fig. 1 deploy with git: Illustrazione della tecnica push – post – receive
Questa metodologia è stata inizalmente sviluppata da Heroku.
Deploy with git: preparazione dei repository
In questa versione personalizzata, a differenza dell’articolo di riferimento (nel quale si mette tutto in localhost):
lasceremo il repository git “myProject” in localhost;
creeremo il repository git “push” in mydomain.com
la directory “stage” ovviamente in mydomain.com
come illustrato in Fig. 1.
me@localhost:$ cd IdeaProjects/myProject
Ora dobbiamo spostarci in remoto e qui è indispensabile avere l’accesso shell al server.
Qui inizializziamo un repository detto “bare” (nudo) che avrà solo la funzione di passamano tra il Git locale e la directory di deploy in remoto
Questo repository myProjectPush.git non conterrà il codice sorgente, bensì farà da passamano tra il client Git a bordo del nostro server di sviluppo e la cartella di staging “stage” nel server di staging
Ora nel Git locale dobbiamo creare un riferimento al repository remoto di push che chiameremo “push” (magari non è la scelta più felice… ma basta fare attenzione):
A questo dobbiamo istruire il Git remoto perché copi i file nella directory stage
Deploy with git: impostazione Push-to-Stage
Dopo aver creato il repository Git remoto dobbiamo scrivere un programma/script che istruisce git su come trattare i file che arrivano dal Git locale. Nella directory hooks della cartella myProjectPush.git ci sono gli script che Git lancia al verificarsi di un particolare evento. Ce ne sono già parecchi di esempio scritti in linguaggio bash, ma per questo scopo si può usare un qualsiasi linguaggio, nell’esempio che ho trovato è scritto in Ruby. Ma prima di tutto creiamo il file:
myUser@remotehost:$ cd myProjectPush.git/.git/hooks
myUser@remotehost:$ touch post-receive
myUser@remotehost:$ chmod +x post-receive
Il “gancio” che ci serve è quello che ci collega all’evento post-receive che è l’evento che accade quando il repository remoto riceve e accetta un push da parte di un repository di sviluppo. Quindi è necessario che il sistema operativo veda il file come uno script eseguibile.
Poi apriamo con l’editor che vogliamo il file post-receive e copiamo incolliamo questo programma Ruby (accertiamoci che l’interprete ruby sia installato con un comando “which ruby”)
#!/usr/bin/env ruby
# post-receive
# 1. Read STDIN (Format: "from_commit to_commit branch_name")
from, to, branch = ARGF.read.split " "
# 2. Only deploy if master branch was pushed
if (branch =~ /master$/) == nil
puts "Received branch #{branch}, not deploying."
exit
end
# 3. Copy files to deploy directory
deploy_to_dir = File.expand_path('../stage')
`GIT_WORK_TREE="#{deploy_to_dir}" git checkout -f master`
puts "DEPLOY: master(#{to}) copied to '#{deploy_to_dir}'"
# 4.TODO: Deployment Tasks
# i.e.: Run Puppet Apply, Restart Daemons, etc
Vediamo cosa fa questo script:
Quando viene attivato lo script post-receive, Git gli passa in STDIN tre parametri: ID commit HEAD precedente, ID commit HEAD presente e il nome del branch da trasferire. Assegnamo questi tre valori alle variabili from, to e branch.
Facciamo in modo che vengano pushati solo commit sul master branch (quello principale); questo sarebbe ragionevole per un push finale verso un server di produzione, ma io adotterò questa tecnica solo per lo staging, quindi rilasso questa parte (posso fare anche push di branch di bugfix per esempio).
La prossima cosa da fare è fare un checkout dal branch attuale alla cartelle “stage”, cioè una copia dei file fisici nella cartella stage.
Il lavoro è finito; sarebbe possibile completare lo script con comandi che dovessero rendersi necessari, come un restart del web server (se ne abbiamo la facoltà) o cancellare file di cache. Ma intanto mi fermo qui.
Salviamo il file e vediamo cosa succede.
Deploy with git: test con Pushing
Possiamo testare facendo un commit e un push verso il remote “push”:
me@localhost:$ git commit -m 'Test push-to-stage'
me@localhost:$ git push push master
muser@mydomain.com's password:
Counting objects: 510, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (477/477), done.
Writing objects: 100% (510/510), 18.16 MiB | 148.00 KiB/s, done.
Total 510 (delta 260), reused 0 (delta 0)
remote: Already on 'master'
remote: DEPLOY: master(915c38ce6cd49905ccff2abf4d9fea9a6ad61a3d) copied to '/home/myUser/domains/mydomain.com/public_html/stage'
To mydomain.com:public_html/myProjectPush.git
* [new branch] master -> master
Nell’output, le righe che iniziano con “remote:” sono quelle generate del nostro script! In particolare vediamo che è stata fatta la copia nella cartella stage.
Ho realizzato questo semplice cruscotto che riporta sia in forma tabellare che grafica (per solo alcuni indicatori) i dati sull’epidemia di COVID-19.
La situazione viene aggiornata verso le 18 di ogni giorno con i nuovi dati in arrivo dal Dipartimento della Protezione Civile.
Note sull’andamento del Covid-19
Si noti il grafico della derivata seconda: questo da’ una stima dell’inversione di tendenza: quando la derivata seconda è negativa, la curva tende a trivolgere la concavità verso il basso (o la “pancia” verso l’alto), cioè il contagio rallenta (pur continuando ad aumentare).
L’inversione di tendenza come si vede si è verificata più volte in modo anche più pronunciato di questi giorni però ha avuto carattere occasionale. In questi giorni invece la derivata secondna si mantiene negativa per il terzo giorno, anche se di poco, ad indivduare forse una tendenza più stabile.
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! 🙂
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