Friday, October 19, 2012

shell clipboard

Ever wanted to cut something from the shell?
Do you need to paste some information stored in a file into your web browser, but you are too lazy to fire up your text editor, select, and hit ctrl+c?
Are you a shell enthusiast?

Than xclip(1) is good for you. It's a CLI to X clipboard, which lets you store into the clipboard buffer what you type on stdin, or from a file.

But to simplify more the task of cutting and pasting, you can create (if you are using, for example, bash) a simple alias in your .bashrc file like this one:

alias clipboard="xclip -i -selection clipboard"

Then, you can simply copy a file in the clipboard like:

$ cat file | clipboard

or directly write in it:


$ clipboard 
hi, how are you?
CTRL+D

And your productivity will be maximized for sure!!!



Thursday, September 6, 2012

Bash Shell Calculator

Working on the Linux shell has always missed something, in my experience: an easy-to-use calculator.

Yes, there is the almighty bc, but it is everything but immediate to use. So here I'm just sharing an easy to implement wrapper to bc, which allows to easily use your shell as a calculator.

Simply drop these lines into a file called 'calc':


#!/bin/bash
echo "scale=2; $1" | bc; exit

Then simply make it executable and copy it into /usr/bin/:

$ chmod 755 calc
# mv calc /usr/bin


Then, you can start doing your math in your shell, simply like:

$calc 3/2
1.50

Consider that, if you want to use more complex operations, you have to escape any special character, like:

$ calc \(3+2\)/4
1.25

And simply change the number after "scale=" to add more precision to the results!

Sunday, July 8, 2012

Rotate it!

$ \left[ \begin{array}{c} \cos 90 \quad \sin 90 \\ -\sin 90 \quad \cos 90 \end{array} \right] \left[ \begin{array}{c} a_1 \\ a_2 \end{array} \right] = $
$ \left[ \begin{array}{c} a_1 \\ a_2 \end{array} \right] $
(And yes, this was just to experiment with CSS rotations!)

Monday, June 18, 2012

Multilanguage Posts in Blogger

A common use-case is the one where we want to broadcast multiple language content on a blog.

Blogger itself does not provide such a facility, but I'm here presenting a javascript hack which allows posts to be written in multiple languages, and have flags on top of the page to switch among them.

First of all, you have to write within a single post a different version for every language which you want to support. To do this, you must first set the correct environment. When editing a post, you should switch to HTML view first:


Then, suppose you are writing content in Italian and English, you should add this skeleton:

<div class="lang:italian">
Questo è del contenuto in Italiano
</div>
<div class="lang:english">
This is English content
</div>
Then, switching back to Compose mode will show you the two sentences. Replace them with the actual content you want to display in your blog post.

Then, go to the Layout Editor of your blog and add a new HTML/JavaScript block on top of your blog posts:



Enter there the following code:

<script language="Javascript">
function getElementsByClass(searchClass,node,tag) {
        var classElements = new Array();
        if ( node == null )
                node = document;
        if ( tag == null )
                tag = '*';
        var els = node.getElementsByTagName(tag);
        var elsLen = els.length;
        var pattern = new RegExp('(^|\\\\s)'+searchClass+'(\\\\s|$)');
        for (i = 0, j = 0; i < elsLen; i++) {
                if ( pattern.test(els[i].className) ) {
                        classElements[j] = els[i];
                        j++;
                }
        }
        return classElements;
}

function show_en() {
        var it = getElementsByClass('lang:italian', null, null);
        var en = getElementsByClass('lang:english', null, null);

        for(i = 0; i < it.length; i++) {
                it[i].style.display = 'none';
        }

        for(i = 0; i < en.length; i++) {
               en[i].style.display = 'block';
        }
}

function show_it() {
        var it = getElementsByClass('lang:italian', null, null);
        var en = getElementsByClass('lang:english', null, null);

        for(i = 0; i < it.length; i++) {
                it[i].style.display = 'block';
        }

        for(i = 0; i < en.length; i++) {
               en[i].style.display = 'none';
        }
}

</script>


<a href="#" onclick="show_en();"><img src="PATH-TO-EN-FLAG" /></a>
<a href="#" onclick="show_it();"><img src="PATH-TO-IT-FLAG" /></a>

This adds at the top of you posts two flag images, which can be clicked, showing either italian or english content. Now, the last bit of editing entails activating either language as the default one (otherwise, you will see both languages, until one of the two flags is clicked).

To do so, open you Blog's model editor and click "Edit HTML".


A popup telling you about the dangers of this operation will show up. Tell blogger to take it easy, and start editing your model. You should find the <body> tag, and add the following attribute in it:
onload='show_en();'

Selecting which is the default language which you want to display.

For a live preview of how this works, point your browser to CranEntertainment's blog!

Sunday, June 17, 2012

Relative Date in LaTeX

It's usual, when writing documents, to use sentences like "I have been doing something since MM/YYYY". This is no problem.

If, on the other hand, you want to write a sentence like "I have been doing something for XX years and YY months", every time you update your document you have to remember to change that sentence.

When writing documents in LaTeX, you can work this problem out using a relatively simple command:
\usepackage{datenumber}
\newcounter{dateone}
\newcounter{datetwo}
\newcommand{\difftoday}[3]{%
      \setmydatenumber{dateone}{\the\year}{\the\month}{\the\day}%
      \setmydatenumber{datetwo}{#1}{#2}{#3}%
      \addtocounter{datetwo}{-\thedateone}%
      \the\numexpr-\thedatetwo/365\relax\space years and
      \the\numexpr(-\thedatetwo - (-\thedatetwo/365)*365)/30\relax\space months
} 

The package datenumber allows to  handle dates as timestamps, therefore we can use counters to perform operations on them (namely, dateone and datetwo). We set dateone to the current date, and datetwo to the user-specified date.

Then, in our document we can use:
\difftoday{2002}{01}{01}

And, say, if we are on March 3rd, 2012, it will render as "10 years and 2 months".

Multilanguage Documents with TeX

Today I have been working on a LaTeX document which was supposed to produce a document in two versions, one in Italian, another in English. It was a technical report, so a lot of stuff, but not so much text.

The classic way to approach this is to have the document written in one language, and then translate it into the second. This can be fair, until you have to apply changes. Editing two documents in a row can be too much effort-consuming.

Luckily enough, $\LaTeX$ is a programming language, before an office automation tool. So we can use conditional variables to fulfill this task. So, the skeleton of my source looks like this:
\documentclass[10pt]{article}

\newif\ifit
\newif\ifen

\newcommand{\langit}[1]{\ifit#1\fi}
\newcommand{\langen}[1]{\ifen#1\fi}

\entrue
%\ittrue


\ifen\usepackage[english]{babel}\fi
\ifit\usepackage[italian]{babel}\fi

\begin{document}

\langen{This is English text}
\langit{Questo è del testo in italiano}

\end{document}

Basically, I declare two conditional variables, \ifit and \ifen which select which language code must be generated. By uncommenting either \entrue or \ittrue, the source will produce the output in English or in Italian.

To ease the task of writing the TeX source, two new commands are declared: \langit and \langen which accept one parameter (i.e., the text in the corresponding language) and output it only if the corresponding conditional variable is set.

Additionally, depending on which conditional variable is set, the babel package is loaded with the corresponding language.

This allows to work on one single TeX source (which decreases the maintainability effort), but allows to produce documents in multiple languages. Adding new languages is just a matter of creating new variables and commands.

Another interesting this, which does not appear in this example, is the charset. Some languages have different charsets, so it would be interesting to set it accordingly. For the italian case, it would entail adding before the document's begin, the following code:
\ifit\usepackage[utf8]{inputenx}\fi

Friday, June 8, 2012

Thursday, June 7, 2012

Write your own Kernel: BootLoader stub

Since I was young, one of my dreams was to write my own operating system.
It was still the DOS era, and as a young boy I started studying lot of tech stuff trying to figure out what was really happening inside my computer.

Actually, at the time, it was too much 'tech' stuff for me, I didn't have the necessary knowledge to understand the subtle details behind a low-level programming approach.

Then I took my Computer Engineering degree, the technical details are no longer obscure, but since then, the old dream remained just... an old dream!

Yesterday, while messing up with a broken hard disk trying to recover some data, a funny idea came up into my mind, and I launched:


dd if=/dev/sda of=MBR count=1 bs=512 > MBR

This dumped into a file the content of that disk's Master Boot Record (MBR). Of course, I couldn''t resist this, and I disassembled it:


objdump -D -b binary -mi386 -Maddr16,data16 MBR

While staring at that code, my old dream came back to me, and so I started messing around a bit. The outcome, was a small bootloader stub, which is actually working, and which I am here describing... In the future, I will surely spend some time extending this, but this will remain just a project-for-fun!
And yes, this is somewhat reinventing the wheel, but for a low-level programmer, this is really funny!

So, the work setting is this:
As for VirtualBox configuration, I have set a 40MiB virtual IDE Hard Disk, FAT32 partitioned, for future development (my legacy machine will mount a similar one), and a floppy device loading a binary raw image (the actual bootloader + early kernel).

So, let's now start analyzing the BootLoader's early stub. This is a 'stage 1' bootloader which simply prints a "Hello World" message, waits for a keystroke and then reboots the system. No actual loading of any stage 2 loader, nor mode change.

Given the hardware architecture we are targeting, we have some constraints about the bootloader to keep in mind for writing it successfully:
  • The bootloader is loaded by the BIOS (which is loaded into RAM at startup as well), given some preconditions
  • Both code and data must be placed within one sector of the booting disk (the MBR), which is 512B
  • The last two bytes of this sector must have the value 0xaa55, otherwise the sector will not be considered as bootable
  • The BIOS will load the bootloader at address 0x0000:0x7c00, so the code must be relocated to that address, otherwise it won't work.
  • The %dl register will contain the disk number, useful for reading additional data from it. Nevertheless, I am ignoring this up to this up to this stage.
  • Of course, we run in real mode, so we have 16-bit code and we can do almost everything! :)
So, our MBR will contain both code and data. in the future, it will contain some partition table as well, but since it is stored into a floppy disk, we must provide a Disk Description Table (DDT) as well, to make it a valid floppy. Additionally, the first byte of the MBR will be part of the first instruction which will get executed, so we have to properly merge these things. The beginning of our boot1.S code is therefore this:

 .code16
 .text

.globl _start; _start:

 jmp stage1_start
 
OEMLabel:  .string "BOOT"
BytesPerSector:  .short 512
SectorsPerCluster: .byte 1
ReservedForBoot: .short 1
NumberOfFats:  .byte 2
RootDirEntries:  .short 224
LogicalSectors:  .short 2880
MediumByte:  .byte 0x0F0
SectorsPerFat:  .short 9
SectorsPerTrack: .short 18
Sides:   .short 2
HiddenSectors:  .int 0
LargeSectors:  .int 0
DriveNo:  .short 0
Signature:  .byte 41   #41 = floppy
VolumeID:  .int 0x00000000   # any value
VolumeLabel:  .string "myOS       "
FileSystem:  .string "FAT12   "

 stage1_start:

The .code16 and .text are things to make GNU assembler (gas) produce valid code. In particular, .code16 tells the assembler to produce 16-bits code (the default would be 32-bits, of course!) and .text ensures that we have everything into one single section (we don't actually mind which one, as they will be stripped, later on). _start is an actual symbol which describes the entry point for the executable, but we will be using this in a different way. The first instruction, jmp, tells the machine to skip "executing" the DDT, so we can have a correctly executing program, keeping the correct format for a floppy (of course, this bites some bytes out of the small 512B available space).

The code now must setup the runtime stack, since it will allow using the "call" instruction for calling subroutines. This looks like this:

 cli              
 movw $0x07C0, %ax
 addw $544, %ax  # 8K buffer
 mov %ax, %ss
 movw $4096, %sp
 sti

This creates a 4KiB stack space above the bootloader, which is (hardcoded) 8KiB large (544 paragraphs). We use the cli and sti couple, in order to disable and then re-enable interrupts, since it is not safe here to perform any interrupt operation, before having the stack correctly set up.

Then, we display our dummy message, "Hello World", and then reboot.

 cld
 movw $hello_s, %si
 call  print_string
 jmp reboot
 hlt

cld clears the direction flag, so that the internal implementation of print_string will read the string placed into %si from the beginning to the end. The final hlt is actually never executed, but placing it there is a good practice, nevertheless. So, let's see how do print_string function and reboot subroutine work.

1:
 movw $0x0001, %bx
 movb $0x0e, %ah
 int $0x10
print_string:
 lodsb
 cmpb $0, %al
 jne 1b
 ret

This routine is actually nicely optimized in space (remember, a bootloader suffers from space!). The funny part is that its entry point is in the middle of its code!
So, when we call it, a lodsb instruction gets executed, which loads one byte from %si into %al and increments %si by one: we read one character of the string from memory!
cmpb checks whether the byte just read is 0, i.e., if it is a NUL terminating character, the end of the string. If not, it goes executing from the 1: label.
There, we find the int $0x10 instruction, which generates an interrupt and searches into the Interrupt Vector Table the entry 0x10, which is associated with the BIOS teletype function family. The value 0x0e stored into %ah tells the BIOS to activate the screen printing function, which displays on screen exactly the caracter stored into %al, using the old-fashioned Codepage Font (which is usually hard-coded within the BIOS itself).
This process goes on until a '\0' is found in the string, then a ret is executed.

Rebooting the system is far more easy:
reboot:
 movw $reboot_s, %si
 call print_string
 movw $0,%ax
 int $0x16 # Wait for keystroke
 movw $0,%ax
 int $0x19 # Reboot the system

A nice string asking the user to press any key is shown, using the same function as before. Then, a keystroke is waited for, using the BIOS int $0x16 which, having %ax == 0, activates the "get keystroke" BIOS function. The BIOS does not return from the interrupt routine until a key is pressed, and then int $0x19 is executed, i.e. the "bootstrap loader" interrupt.


The end of stage1.S goes like this:

 hello_s: .string "Hello World!\n\r"
 reboot_s: .string "Press any key to reboot..."

 . = _start + 0x0200 - 2
 .short 0x0AA55 #Boot Sector signature

The two strings which we want to display are declared, so that we can load their addresses for the printing function.
Then, the line:
. = _start + 0x0200 - 2
tells gas, the GNU Assembler, to move the location of the code being generated (the '.' variable) 512B after _start, and then 2B backwards, which is 2 bytes before the end of the MBR. At this point, we make gas emit the 0xAA55 signature, which tells the BIOS that the current disk is bootable, and we are done: this is our bootloader stub!

Now, the last part, is to actually compile this code. The GNU Compiling Toolchain is targeted at 32/64-bits executables, so by defaults it usually produces ELF Programs. Which we do not want here, as we just need a stream of bytes representing the instructions to be executed.

gas alone cannot do this: it creates headers and everything and cannot be disabled. But we can rely on ld, the GNU linker, asking it to produce a raw binary. So, the two steps are:

as boot.S -c -o boot.o

which creates an ELF executable, containing our code. Then:

ld --oformat binary --Ttext 0x7C00 -o boot.bin boot.o

which, with some magic, strips every header (--oformat binary) and relocates the code starting from the address 0x7c00 (--Ttext 0x7C00) for the text section, which (considering our source) contains our whole bootloader, whose first instruction jumps to the actual initialization code.

Making VirtualBox launch our boot.bin image, this is the actual outcome:



So, now, the next step is to write a second stage bootloader, and make the first stage one be able to load it and transfer control to it!

Wednesday, June 6, 2012

diff e patch in dieci minuti

Prima situazione: stai cercando di compilare un pacchetto dai sorgenti e scopri che qualcuno ha già fatto il lavoro per te, modificandolo un po' per farlo compilare sul tuo sistema. Il suo lavoro è disponibile come "patch", ma non sei sicuro di come si fa ad utilizzarlo. La risposta è che puoi applicare la patch ai sorgenti originali con un comando chiamato, appropriatamente, patch.

Seconda situazione: hai scaricato i sorgenti di un pacchetto open source e dopo un'oretta di piccoli cambiamenti, riesci a compilarlo sul tuo sistema. Vorresti rendere il tuo lavoro disponibile agli altri programmatori, oppure agli autori del pacchetto, senza dover ridistribuire tutto quanto il pacchetto modificato. Ti trovi quindi in una situazione in cui devi creare da te una patch, e lo strumento necessario è diff.

Questa è una breve guida a diff e patch, che ti aiuterà in queste situazioni descrivendo questi strumenti e come vengono utilizzati nella maniera più comune. Ti dirà abbastanza per cominciare ad utilizzarli subito. Dopo, potrai imparare da te i vari comandi aggiuntivi per il tuo piacere personale, utilizzando le pagine del manuale.

Applicare una patch con patch

Per applicare una patch ad un singolo file, spostati nella cartella dove è situato il file ed invoca patch:

patch < foo.patch

Queste istruzioni assumono che la patch sia distribuita in un formato unificato, che identifica il file al quale si deve applicare la patch. Se non è questo il caso, si può specificare il file da riga di comando:

patch foo.txt < bar.patch

Applicare delle patch ad intere cartelle (forse il caso più comune) è simile, ma si deve fare attenzione a specificare un "livello p". Questo vuol dire che, all'interno dei file di patch, i file cui applicare la patch sono identificati da percorsi che possono essere diversi ora che i file sono situati sul tuo computer, piuttosto che su quello in cui la patch è stata creata. Il livello p dice a patch di ignorare porzioni del percorso ai file, così da poterli identificare in maniera corretta. Nella maggior parte dei casi un livello p corrispondente a uno funziona, quindi si può usare:

patch -p1 < baz.patch

Ti dovresti spostare nella cartella principale dei sorgenti prima di lanciare questo comando. Se il livello uno non identifica correttamente nessun file cui applicare la patch, verifica il contenuto del file di patch per controllare i nomi dei file. Se trovi un nome del tipo:

/users/stephen/package/src/net/http.c

e stai lavorando nella cartella che contiene net/http.c, usa:

patch -p5 < baz.patch

In generale, aggiungi uno per ciascun separatore di cartella (la barra '/') che vuoi rimuovere dall'inizio del percorso, fino a che quello che resta è un percorso che esiste nella tua cartella di lavoro. Il valore che raggiungi, è il corrello livello p.

Per rimuovere una patch, utilizza il flag -R, ad esempio:

patch -p5 -R < baz.patch

Creare delle patch con diff

Utilizzare diff è molto semplice, sia che si stia lavorando con singoli file, sia che si stia lavorando su intere cartelle. Per creare una patch per un solo file, usa la forma:

diff -u original.c new.c > original.patch

Per creare una patch per un intero albero di cartelle, fanne una copia:

cp -R original new

Applica tutte le modifiche che intendi fare nella cartella new/. Dopo crea un file di patch con il seguente comando:

diff -rupN original/ new/ > original.patch

Questo è tutto quello che serve per incominciare ad usare diff e patch. Per altre informazioni, puoi sempre usare:

man diff
man patch


Freely translated from this original post, for my own convenience.

Friday, May 11, 2012

Record audio output

So, here's the story. We live in a web-radio era / audio-streaming world.
The old-days audiotape is no longer helpful in recording sounds around us.

Thus, after some headache, this is my modern "Linux audiotape", which I use to record.

Basically, it relies on pulseaudio (I'm running alsa with pulseaudio on my arch distro).
In particular, this script queries pulseaudio to know which is the first audio sink installed in the system (that is, the first audio source that is currently being directed to the output soundcard) and creates a dummy sink which is used to take the output and redirect it into a file.

Then, since we do not want to fill up our disk with raw pcm directed to the speakers, the signal is pipe'd into an ogg encoder, which silently compresses our data and stores it into disk.

#!/bin/bash

sinksIndex=`echo list-sink-inputs | pacmd | grep index | head -1 | sed 's/^.*index:\s\([0-9]\+\).*$/\1 /'` # Get wanted index

echo "Recording to audioRecord.ogg... CTRL+C to stop."

pactl load-module module-null-sink sink_name=audioRecord
pactl move-sink-input $sinksIndex audioRecord
parec -d audioRecord.monitor | oggenc -b 192 -o audioRecord.ogg --raw -

The only important thing to consider, is that to record exactly what we mean to, we must stop any audio stream in the system, start the one which we want to grab, and then launch this script.

This will work with youtube, webradios, audio streaming services, and so long and so forth. To restore the audio setting, after the recording is over, you must restart pulseadio with:

pulseaudio --kill

And furthermore, the program which was generating must be restarted as well. Otherwise, no sound will be heard from your machine.

And by the way, if you want to hear what's going on in your recording, you can just launch something like 'vlc audioRecord.ogg': it will play you the recording while it's still writing the file! (thank you, Unix File System!)

Thursday, May 10, 2012

Check for logged users

At office, we have a cluster made of several machines used for performance experiments. We are not a lot of people, so we explicitly avoided installing software for launching experiments in a controlled way.

Nevertheless, seldom happens that someone remotely logs in and launches stuff, without noticing, for example, if other users were logged or where running programs, thus invalidating any output from (long) runs.

So, at last, I wrote the following bash script which, upon ssh login to the servers, outputs a fancy colored message telling that someone else is logged in (if any).

#!/bin/bash

#for user in $(last | grep still | sort | uniq);
ME=`whoami`



for user in $(last -Rw | grep still | sed 's/ .*//' | sort | uniq);
do
        if [ "$ME" != "$user" ]
        then
                echo -en '\E[;31m'"\033[1mWARNING:\033[0m"   # Red
                echo -en '\E[;34m'"\033[1m \033[4m$user\033[0m \033[0m"
                tput sgr0
                echo -n "still logged in since "
                echo `last -RFw | grep $user | head -1 | awk '{print $4" "$5" "$6}'`
        fi
done

I've simply added this script to /etc/bash.bashrc, so that every user gets a warning message, if needed.

Thursday, February 2, 2012

Etimologia del Bemolle

Quando si inizia a studiare musica, in un momento non meglio precisato degli studi (non meglio precisato perché nessuno, credo, è in grado di ricordarselo) si entra in contatto con le alterazioni. I bemolle, i diesis e i bequadro prima, i doppi bemolle e i doppi diesis dopo cominciano a comparire sempre più spesso sugli spartiti. E uno ci si abitua in fretta, stonatura dopo stonatura.

E ci si abitua talmente tanto, che i simboletti $\flat$, $\sharp$ e $\natural$ diventano un'abitudine per gli occhi.

Però... Perché un bemolle si chiama bemolle?!

Thursday, January 19, 2012

Installing mldonkey on FreeNAS 8.0 embedded

I have recently been able to successfully install mldonkey on my fresh amd64 FreeNAS 8.0.3 embedded (HP Proliant Microserver) setup. I think mldonkey is a piece of software that cannot be renounced, so I'm leaving here a tutorial showing the steps I followed through to have a correct working install.

Caveat: FreeNAS USB installs give really few space available on the / partition. Consider that installing software in it can fill up all the available space, possibly resulting in system crashes. Before following this tutorial, please take into account the real amount of free space available on your / partition, considering that ~10 MB of space will be used by mldonkey software (at the time this tutorial was written!)

  1. Log as root into the NAS, either through ssh or through the console. If you log in as a regular user, type su and enter root password.
  2. We have to install software into the / tree, so we have to make it first of all writeable:

    # mount -uw /
  3. FreeNAS is pointing by default to a repository which mldonkey is not available to download from. Luckily enough, we can find a working binary in an upper level of the repository tree. So, install it this way:

    # pkg_add -r ../net-p2p/mldonkey-core-3.0.6

    Please notice that you have to put the current available version in order to correctly downlaod it. If errors about not finding the package are shown, point your browser to the net-p2p folder in the repository and check which package is available.

    The download/install procedure will actually take a while, as installing on the USB stick is a slow process.
  4. Now log into the web interface and create a new user under Account > Users > Add User

    Username: mlnet
    Password: *****
    Home Directory: /mnt/SHARED-DISK/mlnet/

    Where SHARED-DISK is your storage mount point.
  5. I use my station as a server, so there are multiple users in it. To let all of them get access to the downloaded files, change permissions from a ssh/console shell accordingly:

    # chmod 760 /mnt/SHARED-DISK/mlnet/
  6. Now we have to configure the downloads.ini file. First of all, we have to let mldonkey create its configuration files. Log in (from either ssh or console) as mlnet, using the credentials created at step 4.
    Launch mldoney:

    # mlnet

    The program will setup and then hung, waiting for connections. Kill it with CTRL + C.

    Now cd into /mnt/SHARED-DISK/.mldonkey and edit the downloads.ini using vi, ee, or your favorite shell editor.

    Find the line:

    allowed_ips = ["127.0.0.1";]

    After the semicolon, add "your ip". Note that you can, for example, allow connections from a subnet in your LAN, specifying the correct subnet mask like "192.168.1.0/24"
  7. Now we want to tell FreeNAS to automatically launch mldonkey at startup. We will be using rc.local to this end:

    cd /conf/base/etc/
    vi rc.local (this will create the file if not already existing)

    At the end of the file (or at a suitable place according to your configuration) enter the line:

    su mlnet -c mlnet &

    This will launch mldonkey as mlnet user and continue the startup of the machine.
  8. When the machine is fully booted, access mldonkey url:

    http://YOUR-NAS-IP:4080

    Now, you can start using and configuring mldonkey according to your needs!

Friday, January 6, 2012

Epifania

Se i Re Magi fossero onorevoli
Rinuncerebbero a tutti i convenevoli
A Erode direbbero: "Maestà
Ti diamo il Bimbo, lasciaci l'indennità!"