SSH and Ubuntu Touch - everything you want to ask but are afraid to ask



Introduction

SSH stands for Secure Shell and allows you to access remote machine's shell to execute terminal mode commands in it. Here we will emphasize the use with Ubuntu Touch which will render your UT device a hole lot more useful and powerful. Also, more convenient to work with.

Windows has equivalent solutions, that I will not cover here. I will only tell you that to connect to UNIX shell over ssh from Windows machine, you can use PuTTY as ssh client, but won't at all cover Window's server side for remote shell.

Table Of Conent

SSH on Ubuntu Touch

This chapter will cover two topics:
  • enabling ssh service
  • allowing access to clients

Enabling ssh service

by default your UT device should have the service installed, and possibly running too. To make sure, you check the service status, no need sudo for that, in your Terminal app just issue this:
$ service --status-all | grep ssh
and you should see ssh service listed, with one of 3 possible statuses, marked by a symbol in square bracket:
  • [-] meaning that the service is not running
  • [+] meaning that the service is running
  • [?] was not possible to determine - if you care to understand this particular scenario, just know that it means that the service command was not able to determine the status of the running service since it did not find the status line in the related script in /etc/init.d file. You see, a service usually is controlled via a script in the folder mentioned (but can be a binary executable as well, same logic, just no way for you to inspect) and it is supposed to implement behaviors to few standard commands, one of which is status command. You see the "service" itself is a bash service and has a part like this:
    if ! grep -qs "\Wstatus)" "$SERVICE"; then
        #printf " %s %-60s %s\n" "[?]" "$SERVICE:" "unknown" 1>&2
        echo " [ ? ]  $SERVICE" 1>&2
        continue
    
    which indicates that it will mark the status of a service as [?] if it does not find a line which has the word status after any non-word character in the related service's file in /etc/init.d.
So on my Nexus 5 running Ubuntu Touch, I simply used the default Terminal app and here is the output:
so you can see that the ssh service is running (it has the [+] status). If this is also your case, then it means there is nothing for you to do. If it is not your case, then you should either enable the service, or install it. If ssh is installed, just not running, you can control it with the service command:
#to restart (will start it whether it's already running or not):
$ sudo service ssh restart
#to start service:
$ sudo service ssh start
#to stop service:
$ sudo service ssh stop
I myself usually use restart command, because it doesn't care whether it is already running or not. Also, sometimes you want to restart ssh service even though it is already running, for example if you changed its settings and want the change to take effect.
If you do not seem to have ssh installed in the first place (or if above method does not get you to have ssh service running) you may want to install the service on your device:
#the Android/Ubuntu Touch specific way:
$ sudo android-gadget-service enable ssh
#the general Ubuntu way:
$ sudo apt-get install openssh-server
In the above snippet I am showing you 2 methods as a better reference, but on the UT device you will want to use the 1st command. One reason is that it is a dedicated command to be used on your device, and second being that in order to use the 2nd command, you will also have to make sure that your system partition is writable, which it is not by default on UT. However, once you get ssh service up and running on your UT device, you will then perhaps your Ubuntu desktop or (Raspberry Pi) to also have that service available, and then you will want to use the 2nd command.

Allowing access to clients

On the UT device, by default there is a pre-existing ssh service configuration that is somewhat more restrictive than the default configuration on Ubuntu desktop. The main difference is that on UT the password authentication over ssh is disabled. That means, that clients will not be able to connect to your UT device over ssh using password authentication. Instead, UT is configured to only accept key based authentication. That means that you will need your client to:

  • have a private/public key pare to use - on your client machine you can generate the key pair from command line like so:
    #default way:
    $ ssh-keygen -t
    #increased encryption difficulty:
    $ ssh-keygen -t rsa -b 4096
    
    again, I showed 2 different variants, it is all up to you. Generally speaking the rsa keys are by default generated with a 2048 bit key, but I also show you how you can increase the length key to gain a key with stronger encryption. Just remember that increased key length also means slower decryption during your ssh connection. There is a trade-off between security and speed, and you yourself be the judge. Personally I use default keys for LAN scenario when I know the LAN, and 4096 bit keys for WAN and public networks. But it's not up to me what you choose.
    Note, that when you generate key pair using this method, you will be asked where to save it (save as what file) and also you will be asked for a password to be used to encrypt your key with. As far as it goes to what to save your key as, I usually leave it at the default. The advantage is, that when using ssh command, I do not need to explicitly tell which key file to use, because ssh will check the default location first. As for the password, it is up to you, but since the keys themselves are considered already higher security than a regular password authentication, I don't use password with them when meant to deploy them to my UT devices. Simply, at the key generating prompt I leave the password unset, and hit Enter two times at password and confirmation prompts. The added value is, that I can script a lot of things over ssh easily when I don't need to worry for there to be a password prompt. Essentially keys are often used exactly for that reason, to script remote operations without having to deal with a password.
  • deploy its public key to the UT device - there are 3 methods I want to cover here, the first one being the most default one on Ubuntu in general, yet not quite useful for Ubuntu Touch:
    #defaul ssh deployment way for Ubuntu in general, run from the CLIENT machine:
    $ ssh-copy-id phablet@192.168.0.100
    #phablet is default username on UT device, and IP well, use whatever IP your UT device has
    
    and the problem with this method is, that it first uses a traditional password authentication to connect to the server and install the public key on it. And since UT is configured to reject password authentication over ssh, so the ssh-copy-id command will fail to deliver the outcome.
    You will need to reverse the direction, and instead of uploading the key from the client to the server, instead, download the key from the client to the server. Off course also over ssh, and so for that one to work, your client machine will also need to have a ssh service running. So here is how this would work:
    #These commands are all to be used on the server (UT device)!
    #find out what is the remote home directory:
    $ export HOMEDIR=$(ssh kris@192.168.0.101 'echo $HOME')
    #then construct the key full path:
    $ export KEY="$HOMEDIR/.ssh/id_rsa.pub"
    #then fetch the key:
    $ export KEY=$(ssh kris@192.168.0.101 "cat $KEY")
    #finally import that key:
    $ echo "$KEY" >> ~/.ssh/authorized_keys
    
    this script has to be used precisely, if you care to inspect you will notice how two different types of parentheses are used there in the 1st and 2nd ssh calls, all that is important. Also this script just assumes that there are no errors, that the client machine can be connected to, and that is has a public key that can be imported. The other (3rd) way is to used a single command to handle the script above:
    #ssh-pull-id hosted at: https://sourceforge.net/projects/ssh-pull-id/
    $ ssh-pull-id kris@192.168.0.101
    that way though requires you to first have the ssh-pull-id command. I have hosted it open source under very permissive MIT license here: sourceforge.net/projects/ssh-pull-id/, you can either download binary file and use, or you can compile it from sources yourself. Either way, the ssh-pull-id does just what ssh-copy-id does but in reverse order: instead of working from client to server, it works from server to client. Perhaps I will be able to talk Ubuntu Touch maintainers to include it by default on UT in the future, but for now this is how you can install it onto your UT phone/tablet:
    #download:
    $ wget https://sourceforge.net/projects/ssh-pull-id/files/ssh-pull-id.tar.gz/download -O ssh-pull-id.tar.gz
    #extract:
    $ tar -xvf ssh-pull-id.tar.gz
    #just in case (should not be neccessary normally):
    $ chmod +x ssh-copy-id
    
    and after that you can use it to authorize your client machines to ssh into your UT with just one command each. Just remember that in order to use this command, you will need to first cd into the folder where you have it installed (or add its location to your PATH), but if you want to add it to a global PATH, you can always copy it to /usr/bin/:
    #make UT system partition writtable"
    $ sudo mount -o remount,rw /
    #copy (or move) the file:
    $ sudo cp ./ssh-pull-id /usr/bin/
    #revert the system partition back to read-only:
    $ sudo mount -o remount,ro /
    
only then your UT device will recognize your client connection as known and trusted and will allow it to connect and use ssh session. There still may be need for password upon connection, but it will not be a user password of the UT device, but rather a password with which your key is protected - if you have in deed used a password to encrypt it.

SCP

SCP is SSH+CP, so a command to copy files/directories over ssh. Even though you can always connect your UT device to your PC with a USB cable and transfer files, it can be very convenient to be able to do the same over WiFi, and either direction: from UT to another machine, or from another machine to UT. The only thing you need to remember is that the machine into which you want to scp to has to have ssh service running (must be a ssh server). The quick usage tutorial:
#to copy a file:
$ scp my_file.7z kris@192.168.0.101:/home/kris/
#to copy a directory:
$ scp -r ~/Pictures kris@192.168.191:/home/kris/Pictures/pahblet/
#Warning: the path must exist on the remote machine, otherwise scp will fail
#so if it dos not exist you can first call this one-liner before you call scp:
# $ ssh kris@192.168.0.101 'mkdir /home/kris/Pictures/phablet'
just be aware of few culprits:

  • do not use "~/" notation (or any other notation that deals with environmental variables) in the 2nd part of the scp command (the destination path), because in the command the "~/" will resolve to your local interpretation, not the remote one. That is precisely why in the [deploy its public key to the UT device] chapter I fetched the $HOME variable from the remote machine instead of using the "~/" notation which would give me the local $HOME instead.
  • the destination path must exist on the remote machine before you can scp files/directories into it, because spc command will not create the destination path for you. You can either call an explicit ssh+mkdir command beforehand like in the comments of the example script above, or you can instead use rsync command which does take care of creating folder on the remote machine while its calling syntax is basically same as scp:
    $ rsync -r local-dir remote-machine:path
    just note that old versions of rsync required the -e ssh flag to make it behave like scp because it defaulted to the obsolete RSH protocol. So make sure you are using recent rsync. Even then, scp and rsync share some flags, but there is only a bit of overlap. 
  • when using SSH as the transfer protocol spc (and also rsync) will assume there is a user with the same name as your local user on the remote machine by default - unless you specify the user explicitly. That is why in the scripts above we do specify usernames, because on UT it is "phablet" and on the remote machine it is not.

SSHFS

A step up in the convenience of copying/moving data between machines (including UT devices) is ability of mounting a filesystem from a remote machine locally over ssh in a manner similar to how you would do it with FTP or SAMBA/CIFS or NFS. For example right now, I am writing this very blog from my Ubuntu desktop, and when I need to include a screenshot from my UT device, I just make the screenshot on the phone (VolumeUp+VolumeDown simultaneously) and then access that file from my desktop in nautilus like this:
notice that my local nautilus is now browsing the filesystem on my Nexus 5 running Ubuntu Touch. You even can see in the address bar the part "phablet on 192.168.1.52 Pictures/Screenshots". That is my phone's filesystem accessed from my desktop. I can now drag files/folders from/to it. I can even use Ctrl+T to open another tab in the same nautilus window with a local folder and just copy files/directories between the two:
so you can see two tabs, on the left the files from my Nexus 5 phone, on the right my local ~/Pictures folder, and a great deal of convenience in a multi-device file management over the network!
But to be able to do this in the first place, I first need to add support for SSHFS on my Ubuntu desktop:
$ sudo apt-get install sshfs
after that you need the remote machine to have the ssh service up and running, and finally when you go into nautilus, you need to manually type the ssh location in the address bar. To access the address bar in nautilus, just use Ctrl+L shortcut:
As you can see in the address bar I have the ssh:// uri pointing to my path on the Nexus 5 phone. When I hit enter on a valid URI it will take me to it, and with sshfs installed the ssh:// uri will be translated automatically into sfpt:// uri:
the sftp:// uri stands for Secure FPT, and sshfs package adds support in Ubuntu for mounting it into the local filesystem over the network. SFTP is basically FTP over SSH.
Now what I have done, is that I used nautilus' Ctrl+D shortcut to bookmark this sftp location, and then I don't need to use Ctrl+L and typing the uri by hand each time. I just scroll down in nautilus on the location list on the left, and use an icon to access my phone's filesystem:

and since I am using paswordless key based authentication between my desktop and my Nexus 5, so I do not need to use any password. Otherwise upon connection you would see a password prompt, and you can choose whether or not to remember your password and for how long. Also you can rename your bookmark to whatever you so desire.
And you can use nautilus' Ctlr+H shortcut to toggle between showing and hiding hidden files:
This becomes super convenient if you want to manage some of your phone's config files from the comfort of your desktop. And the crazy convenient thing is, there is no downloading/editting/re-uploading involved as far as your user experience goes. The remote filesystem of your Nexus 5 is now locally mounted, and your OS thinks of it same way as if it was a local folder. So I can just right-click my phone's .bashrc file and open in Geany, edit like a local file, then just save. No downloading/re-uploading needed at all:
And if that did not convince you yet, than I don't know what will :D
And finally, I would love to give an extra piece of info out to all hackers and hobbyists even before you think about asking it: the local mountpoint for the remote sftp filesystem goes under the /run/user/{your_uid}/gvfs/ directory, so if your uid is 1000 (mine is), then you would be looking under /run/user/1000/gvfs/ folder. If you not sure about your uid (ie you're not a hacker I though would be interested in this), then:
$ id -u $(whoami)
and the most convenient way to find full path, is to drag any file from your nautilus onto a terminal window:
so after I dragged my phone's .bashrc file onto a new terminal window I can now see clearly that it's full local path is:
/run/user/1000/gvfs/sftp:host=192.168.1.52,user=phablet/home/phablet/.bashrc
ain't that an ugly one! So, when would you need to use that? To be honest, if you don't know this, then most likely you do not need it ;)

X forwarding

Spoiler alert: run apps off of the UT device onto your desktop (Linux/OSX/Windows)

now this is IMHO a killer application for SSH and also it is what brings the concept of Convergence to us albeit not quite the same way Canonical has envisioned/pictured it:


Instead it is the simplest realisation of convergence concept that additionally depends but on the most generic tech built into GNU/Linux OS, namely, X server + ssh. OK, I need to elaborate here a bit, because it is in fact X server on a remote machine that will be used, not local one. But still, all the x11 libraries, including widgetsets etc, all must exist on the local end.

Now the tricky part: some of you immediately thought of one culprit, that Ubuntu Touch runs on Canonical's very own Mir display Server, which is replacing X server, so how to use X forwarding without X?! *Even though we will be using X server of the remote machine, but still.

Well, the good news is, that UT still ships with X server. Just that it does not use it as a default display manager (it uses Mir instead). It uses the X server for compatibility with applications that depend on X server, and applications that use widgetsets not natively ported to Ubuntu Touch, like GTK+, or GTK3. Currently the only widgetset natively ported to Mir is Qt5 (but not version 4), but some work on GTK has already been started in the past. For the compatibility reasons, Mir also ships with Xmir, which is an emulation tool of sorts, which emulates x server to an application and lets the application to render itself within it. The app will effectively be fooled into thinking that it runs on a regular x server, while in reality it is run in an emulated x server, being render onto a window of Xmir app. So because of Xmir, the x server part is actually included in the Ubuntu Touch system. Xmir has some disadvantages though, most notably the rendering performance is not as fast as native rendering of Qt5 apps, due to lack of hardware accelerated graphics in Xmir, at least on ARM platform (apparently it is present on x86/64 Xmir). This may or mey not be addressed in the future.

Now, Canonical apparently only anticipated users using legacy apps via Xmir, but not directly. Well, maybe not "directly" as in "on the device's screen" which (the device) is running Mir after all. But here we talk about X forwarding, and the proof that Canonical has not thought about that either, is that it never included one tiny tool into the Ubuntu Touch system image, which is responsible for initializing X forwarding over ssh: xauth. Well, the good news is, that after my numerous requests, UBports which is now the maintainer of Ubuntu Touch (after Canonical abandoned it) has informed it that xauth has been approved to be part of the default system (along with few other things). So in the future versions of UT you will be able to take advantage of it, but for now, you still need to install it manually onto your phone:
#make your system writable:
$ sudo mount -o remount,rw /
#install xauth:
$ sudo apt-get install xauth
#revert your system to read-only:
$ sudo mount -o remount,ro /
Now, upon first time you connect to your UT device from another machine over ssh with X forwarding enabled, you will see an error message regarding .Xauthority file, but xauth tool will take care of that automatically, and will generate the file for you.
So in order to enable X forwarding with ssh, you need to either use -X or -Y option with your ssh command:
# either:
$ ssh -X username@hostname
# or:
$ ssh -Y username@hostname
The difference is that -Y switch enables trusted X11 forwarding.  Trusted X11 forwardings are not subjected to the X11 SECURITY extension controls. Some more discussion on the topic here:


Personally, I almost always use -Y switch, but it's all up to you. 
Also, I sometimes use -C switch which requests compression of all data (including stdin, stdout, stderr, and data for forwarded X11 and TCP connections).The compression algorithm is the same used by gzip, and the “level” can be controlled by the Compression Level option for protocol version 1. Compression is desirable on modem lines and other slow connections, but will only slow down things on fast networks. In conjunction with -X/-Y  switches we get:
# either:
$ ssh -XC username@hostname
# or:
$ ssh -YC username@hostname
It is up to you to test whether you get faster/smoother user experience with or without compression turned on.

Now the main part, what can you do with it? Well, you can start any x application from one machine onto the screen of another, over the network! Meaning, the application will still "happen" on your local machine, but its entire GUI will be sent onto a remote screen, and the user will then interact with it there, using his/her keyboard/mouse or whatever input is available on that remote machine.

In short you can either start apps off of your UT device onto the screen of your desktop (Linux, OSX or Windows), as well as you can do the other way around, and start your app off of your desktop Linux and onto your phone's screen: Desktop Apps on Ubuntu Phone .

The former, though, is what brings us to realizing the convergence concept with our Ubuntu Touch device. The way I am using it, is that on my UT device I install some legacy apps, like (in no particular order): Lazarus IDE, Geany, Inkscape, Thunderbird, GIMP, Libre-Office, etc.
Then, I arrive at any location where I also have access to a desktop/laptop computer, like home, or office, and as soon as my UT device connects to the same LAN, I can start these programs off of my phone and onto my PC/laptop's screen!

Effectively my programs, my data and my configuration travels with my in my pocket. Programs run off my phone, data is being kept on my phone, and the configuration is as well, means I do not need to re-configure my programs ever time I install them on a new computer.

Most of these programs I don't even intend to use on the phone's screen, because as x11 apps, they were not designed to be mobile-friendly. Their UI becomes cumbersome on high DPI screen with touch input and on-screen keyboard. Some, I still use on the phone itself via Xmir, particularly useful and practical is Geany, I wrote about it here: Geany on Ubuntu Touch device as text editor, source code editor, debugger and compiler for multiple languages.

Another great use case of mine, is that I have Lazarus IDE installed on my UT devices, and I use it to natively write apps for UT on UT. For example here are my 2 phones Meizu MX4 and Nexus 5, both running Ubuntu Touch, the Nexus 5 (on the right) runs a snake game which I have written myself using Lazarus IDE shown running on the Meizu MX4 (left):
This is just an example, I wrote couple of games and apps this way and I even wrote about it on my blog already:

But although it is possible for me to use Lazarus IDE on-device, meaning, having it running on the phone's screen, it is not what I usually do (unless forced to by not having access to a pc/laptop). Normally, when I say that I wrote some app on UT, it means that I wrote it in Lazarus IDE running off of my UT phone, but x forwarded onto my laptop, where I use it from the convenience of my bigger screen and full sized keyboard.

And my example scales up to any possible applications you might want to try: X applications on Ubuntu Phone
Installing these apps onto UT device comes with a set of problems to deal with, like read-only system partition and limiter size on it even if we remount it as writable. There are ways to deal with it, but not within the scope of this article. To just give you a hint though:
  • install apps somewhere into your home folder than use ln -s to symlink files/folders to your system partition into expected location, because that way you will not use up the space on that location.
  • install your apps into a cantainer, use chroot, xlc, docker or something like it. I wrote about containers on UT here: Easy containers on Ubuntu Touch with qemu-debootstrap
  • use Libertine
Final note is that you are not limited to use x forwarding from your Linux machine. It is also possible for OSX,Windows and some other OSes to act as a X server (where the remote app show up on):
  • on Windows - I already mentionned PuTTY ssh client, but you can also install xming which will be your x server, and will allow you to run graphical apps remotely from anohter Linux machine
  • on OSX - I never used x server on OSX but there are X server implementations for it, one of which is XQuartz, but I can't review it because I haven't tired it myself, so you are encouraged to do your own research

X forwarding over SSH and into chroot containers

On one another occasion I already wrote about containers and X forwarding with them:
Easy containers on Ubuntu Touch with qemu-debootstrap: Configuring X forwarding for containers
That older post of mine also explains how to generate .Xauthority file manually if there is no xauth.

There is a portion that was covered there, as well as a portion that was not covered, which is specific to a scenario when you ssh into a machine with X forwarding, then chroot into a container on it, and then want to run GUI apps from that container all the way back to your ssh client machine. That missing part is about magic cookies, and some other places that also write about it are:

From Wikipedia:
The cookie-based authorization methods are based on choosing a magic cookie (an arbitrary piece of data) and passing it to the X display server when it is started; every client that can prove having knowledge of this cookie is then authorized connection to the server.
These cookies are created by a separate program and stored in the file .Xauthority in the user's home directory, by default. As a result, every program run by the client on the local computer can access this file and therefore the cookie that is necessary for being authorized by the server. If the user wants to run a program from another computer on the network, the cookie has to be copied to that other computer. How the cookie is copied is a system-dependent issue: for example, on Unix-like platforms, scp can be used to copy the cookie.
The two systems using this method are MIT-MAGIC-COOKIE-1 and XDM-AUTHORIZATION-1. In the first method, the client simply sends the cookie when requested to authenticate. In the second method, a secret key is also stored in the .Xauthority file. The client creates a string by concatenating the current time, a transport-dependent identifier, and the cookie, encrypts the resulting string, and sends it to the server.
The xauth application is a utility for accessing the .Xauthority file.
So that is some background and some external links if you care to study into it, and now time for some TODO steps. Suppose that I just ssh'ed from my Ubuntu Desktop into my Ubuntu Touch phone. Then I am trying to run an app from a container while in an X forwarded ssh session. Something like this:
#we start in terminal on Ubuntu Desktop:
$ ssh -YC phablet@192.168.0.50
#now we're in the ssh session of the Ubuntu Touch phone:
$ gksudo chroot ~/Containers/trusty-chroot geany
In the above example we assume that the x forwarding already works for the ssh session, and if we wanted to run geany directly from Ubuntu Touch, it would work. But le'ts assume that when we try to run it from a container, then it does not work. If the reason is related to magic cookies, the most common error message is:
X11 connection rejected because of wrong authentication.
If you did not encounter that problem, then there's nothing to do, you can still read this section for understanding. If you are encountering this problem, you need to import magic cookie(s) from your host (Ubuntu Touch phone in my case) into the chroot container. Remember, this is assuming that the host has x forwarding working. The most commonly quoted method boils down to this:
#on the host (before chrooting):
$ export MagicCookie=$(xauth list)
#mind that your chroot path is likely different from mine:
$ sudo chroot ~/Containers/trusty-chroot bash -c "xauth add '$MagicCookie'"
However this command will fail if you have more than one magic cookie. Because xauth does not allow for adding multiple magic cookies in one call. You can either import all of the magic cookies one by one, or, as I prefer, only export the one that is being used for the current ssh session:
#create Magic Cookie Filter (only currently used one):
$ export MCFilter=$(echo $DISPLAY | cut -c10-12)
$ export MCFilter="unix$MCFilter"
#now only export the one magic cookie we want:
$ export MagicCookie=$(xauth list | grep "$Filter")
#and import it in the chroot:
#mind that your chroot path is likely different from mine:
$ sudo chroot ~/Containers/trusty-chroot bash -c "xauth add '$MagicCookie'"
And that should solve the problem for you. If it still doesn't just export all magic cookies to a file, and then manually import one by one.

Launchers for remote apps

This is something I already covered in my other (already mentioned here earlier) post: Desktop Apps on Ubuntu Phone, but in that post I used this to run apps from desktop onto the phone.
Using same approach you can reverse it, to run apps from the phone onto the desktop. Any direction really. I encourage you to read the linked article for detail, but in short, you create a launcher (a clickable icon) for your remote app, that seats on your desktop or which you access from your Ubuntu Dash, and when you click on it, it starts your remote app.
To condense the information from the linked post to the minimum, the .desktop files used as launchers on Ubuntu, are in fact plain text files, and you can edit them. Among other things they define a parameter called Exec, and it is where you point at your executable or script. You can also, however, fill it with a ssh command that starts your remote app, and it will work.

Comments

  1. Hi Kris, I am not a coder at all but I would still like to install Ubuntu Touch on my Nexus 5. I work on a Mac but have acces to PC's and linux machines as well. How did you flash Utouch to your phone? Tried the UBports-installer but get stuck each time....
    I would appreciate your help.

    ReplyDelete
    Replies
    1. well, try to reach out to me directly on Telegram, I will try to assist you. My handle is @KrisJacewicz (https://t.me/KrisJacewicz)

      Delete

Post a Comment

Popular posts from this blog

Lazarus IDE on ARM Ubuntu (Raspberry Pi, Ubuntu Touch, etc)

Desktop Apps on Ubuntu Phone