This post is about creating an apt repository to support package distribution for debian and invoking package installation from C#. Although this is specifically aimed at Raspbian this is similar for other debian based operating systems.

This is by no means the only way to create an apt repository, in fact there are tools out there which will handle most of this for you. This post is simply one way to create a repository.

This is part five of a series of posts: click here for the previous post or here for the first post.

What is an apt Repository?

Wikipedia

The Advanced Package Tool, or APT, is a free software user interface that works with core libraries to handle the installation and removal of software on the Debian and other Linux distributions. APT simplifies the process of managing software on Unix-like computer systems by automating the retrieval, configuration and installation of software packages, either from precompiled files or by compiling source code.

Our Use

Our apt repository is used to support distribution of our packages for debian. Where before we generated a cab installer for Windows CE and sent that over via a custom web service call, now we’re generating a deb package (see previous post) and hosting it on a web server which apt understands and can manage.

Creating the Repository

The Server

In order to create the repository we’ll need a host server which is accessible by the local systems. In this the case the server must be accessible over the internet as the devices are located all around the world.

The chosen OS for this server is debian 8 Jessie, simply because this is the latest stable version available and, despite the community’s feelings about Jessie, it’s good to keep with the latest stable version.

Preparing the Server

As this server is used for supporting packages that are in development, in testing and in production we need three different locations within the repository to store these packages. These are named unstable, testing and stable respectively. We will need to create them first.

1
mkdir -p /var/www/apt/stable/main/binary-armhf/Packages mkdir -p /var/www/apt/testing/main/binary-armhf/Packages mkdir -p /var/www/apt/unstable/main/binary-armhf/Packages

The next step is to install apache2, this is the web server that will support the packages being distributed over the network. We will need to modify the config file to look at /var/www/apt and also enable the website.

1
apt-get install apache2 cp -f 000-default.conf /etc/apache2/available-sites/000-default.conf a2ensite apt service apache2 reload

The above command uses a local file called 000-default.conf which is based on the default conf file that comes with apache on installation. Since this server only hosts our apt repository we can overwrite the default config file, however if your server is serving more than purpose in terms of apache you will need to handle things differently.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<VirtualHost *:80>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com

#ServerAdmin webmaster@localhost
DocumentRoot /var/www/apt

# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn

ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined

# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
VirtualHost>

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet

At this point you should be able to navigate to your web server in a browser using the name or IP address assigned and find a display similar to the one below:

Untitled

Within the dists folder we should see three more folders: unstable, testing and stable. These folders should be empty.

Adding a Package to the Repository

First we need to install a package to aid us in generating all the necessary meta data.

1
apt-get install dpkg-dev

Now we can take a package generated in the previous post and upload it to: /var/www/apt/dists/wheezy/unstable/main/binary-armhf/Packages. Most these folder are missing so you will need to create them. After that you can run the following command to generate the necessary meta data:

1
2
rm -f dists/wheezy/$1/main/binary-armhf/Packages.gz
dpkg-scanpackages dists/wheezy/$1/main/binary-armhf/Packages /dev/null | gzip -9c > dists/wheezy/$1/main/binary-armhf/Packages.gz

This will delete the previous meta data, if it exists, and regenerate it. When we later request an updated list of packages from this server via the client this is the file that will be used.

Using the Repository

Updating the Client

In order for the client to be aware of the repository we’ll need to modify our client apt config:

1
/etc/apt/sources.list

To append the following server configuration:

1
deb http://apt.server.com/ wheezy stable/main

The deb key word tells our client this is where packages are stored, followed by the address of the server. The key word wheezy refers to our distribution, which takes us into /var/www/dists/wheezy. The next phrase stable/main takes us further into directory stable/main. If we wanted to change the folder layout within our apt repository we would need to change the way we have defined our server here.

Installing a Package

To the install the package we must first update our local client’s apt package cache. This can be done by calling the following command:

1
apt-get update

We should see our apt repository name show up as the synaptic package manager updates itself. Then, to install the package, we can run a similar command to the one below:

1
apt-get install my_package

This will install a package called my_package. If this name is available from several different repositories we may have a conflict, however for the purpose of our example there shouldn’t be any conflict and our package manager should download the latest package from our repository and install it, executing all the scripts as described in the previous post.

Executed From C

This is a fairly easy step, all we have to do is shell out the above command from C#. To do this we can use the following piece of code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using (var start = new Process())
{
start.StartInfo = new ProcessStartInfo()
{
FileName = "sudo",
Arguments = "apt-get install my_package",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};

start.Start();
while (!start.StandardOutput.EndOfStream)
{
yield return start.StandardOutput.ReadLine();
}
start.WaitForExit();
}

This will shell out the command sudo apt-get install my_package, this needs to be ran using sudo unless the process executing it is already running as root (which is not advised).

This will return once the execution has completed, however as we are updating the executing package it may not return once its execution has completed as part of the install process for the package may restart the daemon. This is entirely down to how you script your deb package however.