sabato 30 giugno 2018

Installare transmission-daemon sul Raspberry Pi, guida per Raspbian Stretch


In occasione del rilascio degli ultimi aggiornamenti di Raspbian Stretch e dopo quasi cinque anni dalla mia precedente guida sull’installazione e configurazione di transmission-daemon, vediamo cosa è cambiato.

Premetto che rispetto a Raspbian Wheezy l’installazione di Transmission-daemon è molto più semplice, in pochi passi avremo il nostro daemon attivo e funzionante.

I requisiti necessari per seguire questa guida sono due:

1. avere un Raspberry Pi con Raspbian Stretch installato, attivo e collegato alla rete con IP statico;

2. avere un hard disk esterno collegato ad una porta USB del Raspberry Pi, formattato con filesystem ext4 e montato automaticamente al boot;

Se la vostra configurazione soddisfa questi due requisiti possiamo iniziare.

Dal terminale del nostro Raspberry Pi (o da remoto via SSH) controlliamo se ci sono aggiornamenti del sistema.

$ sudo apt update

Se ci sono aggiornamenti procediamo con

$ sudo apt upgrade

Quindi installiano transmission-daemon

$ sudo apt install transmission-daemon

Una volta terminata l’installazione procediamo ad aggiungere il nostro utente al gruppo debian-transmission e l’utente debian-transmission al gruppo del nostro utente. Nel mio esempio userò l’utente di default pi, sostituitelo con il vostro nome utente.

$ sudo usermod -aG pi debian-transmission
$ sudo usermod -aG debian-transmission pi

Prima di procedere alla configurazione prepariamo il disco esterno. Andiamo nella directory che contiene quella di mount del disco, in questo esempio il disco esterno è montato in /media/storage, quindi andrò nella directory /media. Modificate il percorso in base alla vostra configurazione.

$ cd /media
Ora modifichiamo il proprietario del disco.

$ sudo chown -R pi:pi storage

Ora l’utente e il gruppo proprietario del disco è pi (ricordatevi di utilizzare il vostro nome utente), l’opzione -R esegue l’azione ricorsivamente all’interno di tutte le directory del disco, quindi tutti i file contenuti in esso saranno proprietà del vostro utente.

Entriamo nel disco e creiamo le due directory che conterranno i nostri file.

$ mkdir complete
$ mkdir incomplete

Ho usato complete per i file completi e incomplete per quelli in corso, siete liberi di utilizzare dei nomi di vostro gradimento.
Correggiamo i permessi di accesso alle directory.

$ chmod 775 complete
$ chmod 775 incomplete

Con 775 abbiamo garantito i permessi di lettura, scrittura ed esecuzione al proprietario ed agli utenti che appartengono allo stesso gruppo, gli altri non hanno il permesso di scrittura.

Ultimo passo la configurazione di transmission-daemon

$ sudo nano /etc/transmission-daemon/settings.json

Qui dobbiamo modificare le seguenti righe:

"download-dir": "/media/storage/complete",

"incomplete-dir": "/media/storage/incomplete",

Ricordatevi di inserire il percorso ed i nomi delle directory della vostra configurazione.
Nella riga seguente assicuriamoci che l'utilizzo della directory per i file incompleti sia abilitato:

"incomplete-dir-enabled": true,

Proseguiamo con le impostazioni per l'accesso in remoto a transmission-daemon.

"rpc-enabled": true,

"rpc-password": "la_tua_password",

Cancellate quanto contenuto tra le virgole ed inserite una password da utilizzare per l’accesso al daemon. La password che ora appare in chiaro verrà successivamente criptata, la prossima volta che apriremo il file non sarà visibile. Per finire inseriamo un nome utente per accedere

"rpc-username": "nome_utente",

Inserite un nome utente di vostra scelta.
Il filtro sugli indirizzi IP dai quali poter accedere non è necessario se limitate l’accesso dalla vostra rete casalinga, quindi lo disabilitiamo.

"rpc-whitelist-enabled": false,

Salvate le modifiche ed uscite premendo i tasti CTRL-X.

Ora facciamo in modo che transmission-daemon legga ed utilizzi le nuove configurazioni:

$ sudo service transmission-daemon reload

Abbiamo terminato, procediamo a riavviare il Raspberry Pi per verificare che transmission-daemon si riavvia al boot.

$ sudo reboot

Provate l’accesso da web da un altro dispositivo collegato alla stessa rete scrivendo sul campo indirizzo del vostro browser il numero IP del Raspberry Pi seguito dal numero di porta 9091 come nell’esempio:

192.168.1.100:9091

Vi verrà chiesto il nome utente e la password, dovrete usare quelle inserite nel file di configurazione.
Provate ad avviare un file torrent e assicuratevi che il file in download venga scritto sul disco nella directory dei file incompleti.


domenica 26 marzo 2017

Multi-threading e interfaccia grafica GTK+

Il multi-threading è la suddivisione di un processo in diversi sotto processi eseguiti in parallelo (o concorrentemente), questo ci permette di eseguire il nostro programma sfruttando la potenza di calcolo di tutti i core presenti nei moderni processori dotati di tecnologia multi-core.

Questa tecnica è molto utile soprattutto per migliorare le prestazioni in presenza di un carico di lavoro pesante, ma è anche utilizzata per preservare la reattività dell'interfaccia grafica in qualsiasi situazione.
Mentre negli anni ottanta era accettabile il fatto di dover attendere davanti al monitor mentre il computer finiva di elaborare l'operazione assegnata, al giorno d'oggi, trovarsi davanti ad un'interfaccia grafica che non risponde a nessun comando ci farebbe subito pensare che il software in esecuzione sia andato in crash o che comunque ci sia un problema.

Quando non è possibile velocizzare un'operazione al punto da renderla quasi istantanea, allora bisogna intrattenere l'utente per ingannare l'attesa. Ad esempio, se l'interfaccia grafica fornisce informazioni sullo stato dell'esecuzione di un'operazione complessa, l'utente attenderà sapendo che il software sta lavorando correttamente.

Iniziamo con il creare una classe che sia in grado di svolgere un compito su un thread separato dal programma principale.



E' una classe molto semplice: ha una variabile membro di tipo double chiamata fraction_done, che ci servirà a simulare l'esecuzione di un lavoro, ed una denominata wrk_mutex, che è di tipo mutex (dall'inglese mutual exclusion, mutua esclusione) fornito dalla standard library per impedire che più thread accedano contemporaneamente agli stessi dati.

Le funzioni membro sono solo due: start_work() che esegue il lavoro e get_fraction_done() che comunica lo stato dell'esecuzione.

All'interno della funzione start_work() troviamo un primo blocco, delimitato dalle parentesi graffe, all'interno del quale si invoca la classe lock_guard, che serve ad acquisire il mutex ed impedire a qualsiasi altro thread in esecuzione di interferire fino alla fine delle parentesi. All'interno di questo blocco protetto, fraction_done è inizializzato con il valore di 0.0.
Successivamente troviamo un loop infinito, all'interno del quale il thread in esecuzione attende 250 millisecondi con la chiamata di funzione sleep_for(), per simulare lo svolgimento di un'operazione complessa.
Quindi c'è un nuovo blocco di codice con una nuova acquisizione del mutex e si incrementa il valore di fraction_done, infine verifica se il lavoro è concluso (al raggiungimento del valore di 1.0) ed esce dal loop.

Ora scriviamo un piccolo programma che utilizza la nostra classe Worker.


Nella funzione main() viene dapprima creata un'istanza della classe Worker, quindi viene creato un nuovo thread che esegue la funzione membro start_work() di Worker. Da questo punto in poi il programma principale è eseguito in modo concorrenziale a start_work(), nel loop infinito all'interno di main() viene richiesto all'istanza di Worker il valore attuale di fraction_done.

Nella funzione get_fraction_done() viene invocata la classe lock_guard per impedire che fraction_done sia modificato mentre se ne comunica il valore.

Quindi viene stampato sul video il valore ottenuto (solo se diverso dall'ultimo ottenuto).
Se il valore ha raggiunto 1.0 allora esce dal loop perché il lavoro è terminato, infine viene invocata la funzione join() per riunire il thread al programma principale prima di rilasciare le risorse allocate e terminare.

Proviamo a compilare ed eseguire


Funziona come volevamo, il thread svolge il suo compito ed il programma principale ci tiene aggiornati sullo stato del lavoro.
Ora proviamo ad applicare la stessa tecnica ad un programma con interfaccia grafica realizzata con la libreria GTK+.

Utilizzerò l'IDE Anjuta per realizzare un nuovo programma GTK+.

Creiamo un nuovo progetto di tipo C++ GTKmm (semplice).
Nelle opzioni del progetto spuntiamo la voce "configura pacchetti esterni"
Spuntiamo il pacchetto pthread-stubs
Dopo aver creato il progetto andiamo a creare l'interfaccia grafica con Glade


Come potete vedere dall'immagine è una finestra con una barra progressiva ed un'etichetta per visualizzare lo stato del lavoro. Subito sotto ci sono i pulsanti per avviare e fermare il thread.

Ora modifichiamo la classe Worker per farla lavorare in un programma dotato di interfaccia grafica.


Analizzando l'header potete vedere l'aggiunta di alcune variabili membro:
- has_stopped - ci servirà per capire se il thread è fermo;
- shall_stop - utilizzato per comunicare al thread l'intenzione di fermarlo;
- caller_notification - questo è un signal che utilizzeremo per comunicare con il programma principale.

Inoltre sono stati aggiunte tre funzioni:
- has_stopped_working() - per sapere se il thread è fermo;
- stop_work() - per fermare il thread;
- signal_caller_notification() - per connettere il signal al nostro programma.

Per finire è stata modificata la funzione start_work(), con l'aggiunta dell'emissione del signal e la possibilità di fermare il lavoro tramite shall_stop.

Ora vediamo il programma principale.


Oltre al file main.cc ho creato una classe Controller che si occupa di gestire l'intera applicazione.
Di solito si trovano esempi o tutorial in C++ che utilizzano GTKmm dove si crea una classe derivata di Gtk::Window e si costruisce l'interfaccia da codice, il mio approccio è differente.
Per prima cosa preferisco creare l'interfaccia grafica con Glade, non vedo il vantaggio di costruirla tramite codice, per quanto riguarda questo pattern, è probabilmente frutto dalle precedenti esperienze di programmazione su OS X con Objective-C.

L'istanza di Controller creata in main() si occupa di recuperare gli elementi dell'interfaccia grafica dal file .ui, quindi in connect_signals() collega i signal dei pulsanti e di due altri oggetti: ctrl_worker e ctrl_dispatcher.
Il primo è un'istanza della classe Worker e collega il suo signal alla funzione on_worker_notification().
Il secondo è un oggetto della classe Gtk::Dispatcher ed è l'elemento fondamentale per garantire il corretto funzionamento del thread di Worker con l'interfaccia del programma principale.
Alla pressione del pulsante start viene creato un thread al quale viene affidata l'esecuzione della funzione start_work() di Worker, il puntatore denominato wrk_thread ne terrà traccia.
Ogni qualvolta il thread emette un signal questo verrà ricevuto dal programma ed eseguirà la funzione on_worker_notification() che, a sua volta, emetterà il signal del nostro ctrl_dispatcher.
Quest'ultimo verrà ricevuto dalla funzione on_dispatcher_notification() che si occuperà di verificare lo stato del thread, aggiornare l'interfaccia grafica ed eliminarlo se fermo.

Questo è il programma perfettamente funzionante.


Provando a non utilizzare l'istanza di Gtk::Dispatcher, collegando il signal di Worker direttamente alla funzione che gestisce il thread ed aggiorna l'interfaccia grafica, finiremmo con un crash dell'applicazione in un momento non precisato dell'esecuzione.