Kurbis

Tech blog

Boot with UUID device

March 06, 2024

In virtualization or when installing Slackware on a device for which you don't know the root device (i.e., /dev/sda, /dev/hda) beforehand, it is safer to use disk UUIDs to refer to disks and partitions.

Determining a disk's UUID requires the disk to be available as a block device (i.e., for a disk image it is first necessary to create a block device from it). For example, the following command outputs the UUID of the block device "/dev/sda1" or, in other words, the first partition of disk "/dev/sda"

$ blkid /dev/sda1

Afterwards, it is necessary to update the bootloader configuration. This depends of course on the bootloader you are using and there are tons of tutorials available out there, for example, Extlinux with UUID device and Lilo with UUID device.

Finally, and most important, the standard Slackware installation, which uses the version huge of the kernel and no initrd, is not capable of booting from a root device through its UUID. Instead, it is necessary to create an initrd and, while you are it, might as well switch to the generic kernel. With the generic kernel and the initrd, your system should be able to boot from a root device using the device's UUID. I don't know if the generic kernel is strictly necessary, I haven't tried with the huge kernel and the initrd, maybe it works too.

Tags: slackware

Parametric polymorphism in ML is broken, especially in what arithmetic operators are concerned. Let's explore the meaning of parametric polymorphism through different points of view and see how these are violated in ML.

Parametric polymorphism means any type instance

We say a function is polymorphic when we have a single piece of code, the function's body, that can be applied uniformly to values of different types. This means that the sematics of the function does not depend on the particular type instance that is picked for its type variables. For example, when we see a function with type

f : 'a -> 'a

we know that f has a single body (a single piece of code) and that this code does not depend on the particular instances that are picked for the type variable a. Therefore, a can be instantiated with any type, for example, int or double.

Now, this is not exactly true for the case of arithmetic operators, say +, is it? In fact, the operator + can only be instantiated with int or double, as all the other instances are illegal.

Parametric polymorphism means a single piece of code

Let's now sidestep from this problem and focus on the property of a single piece of code. As mentioned before, parametric polymorphism means that we have a single piece of code that can be instantiated with different types. Even though + is a primitive, let's think for a second how we would implement it. Let's imagine there are two more primitive functions, namely, plusInt and plusDouble with the types

plusInt : int -> int -> int
plusDouble : double -> double -> double

If we were to use these functions as building blocks for our polymorphic + operator, then we would not have a single piece of code for the operator's body. Instead, we would have two pieces of code, as shown in the following ML/Haskell-like example:

(+) : 'a -> 'a -> 'a

(+) : int -> int -> int
(+) x y = plusInt x y

(+) : double -> double -> double
(+) x y = plusDouble x y

This example is not ML, but I think it's a nice transition between ML and Haskell, as we can think of the two previous definitions as instances for the polymorphic operator +. In any case, this is not parametric because we do not have a single piece of code. This is in fact ad-hoc polymorphism, which in Haskell is achieved by means of typeclasses.

Let's try a different approach and try to build a single piece of code. We are going to see that, once again, it is not going to be parametric.

Parametric polymorphism means types are opaque

As mentioned before, parametric polymorphism means that a given code does not depend on the particular instances of its type variables. This means that whatever a function does to a polymorphically typed argument cannot depend on its type. So what functions can be applied to a polymorphically typed argument? The answer is easy: only polymorphic functions (including itself). This means that a function with type

f : 'a -> 'a
f x = ...

can either diverge or return its argument unchanged. But it cannot do anything else to argument x because everything other than keeping x for a while and returning it will have to depend on the type instance of x, thus making this function no longer polymorphic.

Coming back to the + operator. We cannot add two numbers if we do not know that they are numbers. But, for a moment, let's assume we can. The addition operator has the following type

(+) : 'a -> 'a -> 'a
(+) x y = ...

However, 1 + 2 returns 3. If we were to follow the type of + we would expect that + either diverges or returns x or returns y. Returning 3 is neither, therefore, + is not behaving polymorphically.

Tags: type theory

Enable sudo

February 09, 2014

This post explains how to configure sudo under Slackware.

First, make sure you have sudo installed, given that it is not part of the base installation.

$ slackpkg install sudo

Afterwards, it is necessary to edit the file "/etc/sudoers" and enable one of the sudo mechanisms. Below, I have listed all of the possible mechanisms. They are pretty much self-explanatory.

## Uncomment to allow members of group wheel to execute any command
# %wheel ALL=(ALL) ALL 

## Same thing without a password
# %wheel ALL=(ALL) NOPASSWD: ALL 

## Uncomment to allow members of group sudo to execute any command
# %sudo ALL=(ALL) ALL 

## Uncomment to allow any user to run sudo if they know the password
## of the user they are running the command as (root by default).
# Defaults targetpw  # Ask for the password of the target user
# ALL ALL=(ALL) ALL  # WARNING: only use this together with 'Defaults targetpw' 

## Read drop-in files from /etc/sudoers.d
## (the '#' here does not indicate a comment)
#includedir /etc/sudoers.d

From the sudo mechanisms above, uncomment whichever works best for you. In my opinion, the following sudo mechanism seems to provide a good compromise between usability and security, and after uncommenting it look as follows.

## Uncomment to allow members of group wheel to execute any command
%wheel ALL=(ALL) ALL

At this point, the next configuration steps depend on which sudo mechanism you have enabled in the previous step. If you have enable the sudo mechanism for the wheel group, then you need to add the users you want to give sudo power to the group wheel, if they are not already part of this group. If you enabled the sudo mechanism for the sudo group, then you have to add those users to the sudo group.

In any case, in order to add a user to a group, simply run the following command, where "" is the login name of the user you want to give sudo power.

$ gpasswd -a <user> wheel

It might be the case that the user you are giving sudo power is in fact yourself, or that the users you are giving sudo power are already logged in. Given that the group definitions are only updated when you login, all group updates will require those users to logout and login again.

For example, if user "john" was not part of the group "wheel", and you added "john" to this group, and he was already logged in, then "john" will have to logout and login again in order for the changes to the groups to take effect.

Sources: Adding users to sudoers file

Tags: slackware

This post explores some design and implementation options for a multithread client, in a client-server application, in the context of Linux and the POSIX API, including POSIX threads and signals. Moreover, we are interested in writing some good code and creating a good engineering product, one that allocates resources and frees them when they are no longer necessary. These resources include signal handlers, mutexes, semaphores, sockets, and threads.

And, even though it is simple to acquire and release these resources when they are used individually, when they are used together, the problem of graceful exit, one in which all acquired resources are properly released, becomes far from trivial. And it is such a shame that this is not a trivial problem to solve, given that this seems to be one of the most common use cases in Linux daemon/server programming.

A word on POSIX...

Part 1 - 3 threads: master, input, and output

Let's start with a simple design of our client application. We will be going to collect user input from the standard input, which is going to be connected to a terminal, and are going to send it to the server. At the same time, we are going to collect replies from the server which we will send to the standard output, which is going to be connected to a terminal. This is a fairly simple setup for an initial design.

Let's draw some inspiration from the TTY terminals and let's implement the application as 3 threads: (1) master thread, responsible for acquiring the resources when the application starts and releasing them before the application terminates, (2) input thread, responsible for collecting user input from the standard input and sending it to the server, and (3) output thread, responsible for collecting replies from the server and sending them to the standard output.

So, in a typical run, the master thread will create mutexes, semaphores, sockets, and it will spawn the input and output threads. Then, the master thread will wait for the other threads to finish. Meanwhile, the input and output threads will work on the user input and server replies. At some point, these threads will terminate and will join with the master thread. Then, the master threads proceeds to release the acquired resources and, finally, it terminates the process.

Let's see what it means for the input and output threads to terminate. Let's start with the simplest case, which is the output thread. The output thread is receiving replies from the server and sending data to the standard output. Since we are using blocking IO, the output thread is blocked in a call to "recv" and, if the server closes the connection, this call will either return "0", for an orderly shutdown, or an error code that represents the fact that the connection is closed or broken. In any case, this means there are no more replies to receive and the output thread can terminate.

On the other hand, for the input thread is a bit more complicated because we have to address with two possible cases. The first, the simplest, is when there is no more user data to read from the standard input. So, in this case, the input thread will be blocked on a call to one of the standard C library reading functions, for example, "fgets", and if this function returns "EOF" then that means there is no more user input and the thread can terminate. The other case we have to address is when the input thread tries to send the data inputted by the user to the server. In this case, it is possible that the server closed the connection and a call to "send" will terminate with an error code representing the fact that the connection is broken. In this situation, the input thread can terminate.

Now that we have covered the situations in which the input and output threads terminate, we have to think about how the master thread can join these threads. And this is when things get complicated...

Part 2 - master joins other threads

As I said before, the master is waiting for the input and output threads to terminate. However, we saw that any of the threads can terminate or even both threads can terminate at the same time. And the master does not know which thread will terminate first. Therefore, the master thread cannot simply call "pthread_join" on one of the threads and then on the other because it might block indefinitely.

Moreover, imagine the situation in which the server closed the connection and the output thread has terminated, but the input thread is still blocked in the call to "fgets", waiting for user input. In this situation, we clearly want the input thread to terminate as well, otherwise the user will enter input that cannot be sent to the server and he will only be notified that the connection has been closed long after it has happened. In order to overcome this problem, we are going to look at a solution that uses signals to wake up threads on blocking calls.

Part 3 - signalling input and output threads

Let's revisit the scenario introduced in the previous section. The input thread is blocked on a call to read user input, the output thread is about to terminate due to an orderly shutdown from the server, and the master thread is waiting to join the other threads. At this moment, we need to wake up the input thread and tell it to terminate as well. The output thread has to be the one responsible for waking up the input thread because it is the only running thread. In order to achieve this, before terminating, the output thread will change some global state to indicate that the input thread is to terminate, and then send a signal, for example "SIGUSR1", to the input thread to wake it up. Then, the input thread wakes up, checks this global state, and terminates. Because both threads terminate, the master joins both threads, releases the acquired resources and terminates as well. The world is fantastic! Or is it?

This plan seems simple but in order to get there we need to tackle some implementation details. The first one is signal handling. If a thread is blocked on a system call and receives a signal, this call will be interrupted causing the error code "EINTR" to be returned. Please note that this does not necessarily mean that we want the thread to terminate. It can also be the case that the call was interrupted for another reason. Therefore, at all call sites to system calls, or functions calling system calls, we must check if the error code is "EINTR" and at the same time check the global state to determine whether the call should be repeated or whether the thread should terminate. And, by the way, it is not only cumbersome to update all call sites but it is also easy to forget to check a given call site for the error code and the global state. Finally, we also have to install the signal handler, but that is rather obvious.

Another implementation detail we have to tackle is the change in the global state. We have to consider the fact that the output thread wakes up the input thread, or the other way around. And even though it is not a real problem to send multiple signals to the other threads, this situation should be avoided. Therefore, the change in global state should be synchronized. And, we have to remember that call sites to semaphores and mutexes must also be checked for the "EINTR" error code.

So this plan this like a perfectly reasonable plan and you can even find it in forums throughout the Internet with people advising on this course of action. Unfortunately, it does not work! And the reason why it does not work it is because there is always a race condition that cannot be eliminated this way. The race condition occurs between checking the global state and entering the system call that will block for IO. In other words, when we send a signal to another thread to wake it up, we can't be sure if the thread is already blocking for IO or if it is still running to get there. Of course, you could "sleep" and wait for the thread to block. But that's hand-and-slash, not proper engineering. So let's take a look at other options.

Part 4 - control channel

So the input and output threads block on some file descriptor until there is data available to read or the file descriptor is ready to send data. We could create another file descriptor (e.g., a pipe or a socket) that would function as a control channel. This way, the input or output threads could "poll" or "select" on 2 file descriptors simultaneously, namely, the data channel and the control channel, and when "poll" or "select" terminate they will check if the data comes from the control channel, in which case the threads would terminate. This seems that it could work, although I have not tried to implement it. The problem is that you end up with one additional file descriptor per thread. And if you want your application to scale, you need to save on resources, especially file descriptors. Naturally, this is just a client application and it does make sense to think about scalability, but if you are planning to port some of the design decisions in this post to a server, then you have to think about scalability.

Part 5 - detach and cancel

While exploring the POSIX threads API, I came across with thread detaching and cancellation. These seems to go well together. So thread detaching makes a thread "garbage collectable", in the sense that it does not need to be joined by another thread in order to free up the resources but it is instead free automatically by the operating system when the thread terminates. And thread cancellation means that a thread can be forcibly terminated by another thread when it is blocked on a cancellation point, for example, blocked on a IO call.

With these features, the threads can be created as detached threads (i.e., "garbage collectable") and then, for example, when the input thread is about to terminate, it will cancel the output thread, but synchronized with a change in global state so that the two threads don't cancel each other. The master thread also has to be adapted: so the master thread should be waiting on a semaphore that is posted with the synchronized change in global state.

There is still a race condition but it might be a problem or not depending on what you are doing. The race condition occurs because cancelling a thread is not an immediate action: thread cancellation is a promise than when a thread enters a cancellation point, such as, a blocking IO system call, it will be forcibly terminated. This can be a problem if the master thread, which might be running by now, releases resources that can still be used by the thread that will be cancelled.

Tags: programming

In Slackware, there are different network management programs, including, the Slackware scripts, wicd, and NetworkManager. If using Slackware in a personal desktop or laptop, NetworkManager is a good choice, even if you are not using KDE as a desktop environment or window manager.

Given that NetworkManager is a service, the program runs as a daemon. As a result, you might want to run the client application as well, which is "nm-applet". This applet will create an application in your desktop environment tray, which you can use to control which networks to connect to and also manage network connections in general, including VPN.

Unfortunately, I haven't found a simple way from within the applet to create system-wide connections, that is connections that are available to all users, while asking NetworkManager to save the network key, such as, a WEP or WPA-PSK key. The only way I have found is to execute the "nm-connection-editor" with "root" permissions, as follows

$ sudo nm-connection-editor

By executing the connection editor with "root" permissions, the NetworkManager applet is able to write to the NetworkManager configuration directory, namely, "/etc/NetworkManager", and in particular to the system connections directory, namely, "/etc/NetworkManager/system-connections".

An alternative to executing the connection editor with "root" permissions, is to simply edit manually the files contained in this directory, and in particular the section containing security information. For example, for a standard wireless network, the section might look like the following.

[802-11-wireless-security]
key-mgmt=<Your key management system, such as, WEP, WPA, etc>
psk=<Your private key unencrypted>

Tags: slackware

When working on a complex command line, it's best to use editor. This can be done very simply in bash (AFAIK, might work on other shells as well), simply by defining the EDITOR environment variable and typing C-x C-e (i.e., Control-X Control-E). Afterwards, your editor will appear and you can edit the command line, save the file, and exit the editor. Done!

Tags: linux

Terminal color resources

November 20, 2013

Color resources, with meaning and default values, for terminals, such as, Xterm and Rxvt unicode.

!black
*color0:  #251f1f
*color8:  #5e5e5e
!red
*color1:  #eb4509
*color9:  #eb4509
!green
*color2:  #94e76b
*color10: #95e76b
!yellow
*color3:  #ffac18
*color11: #ffac18
!blue
*color4:  #46aede
*color12: #46aede
!magenta
*color5:  #e32c57
*color13: #e32c57
!cyan
*color6:  #d6dbac
*color14: #d6dbac
!white
*color7:  #efefef
*color15: #efefef

In my configuration for Rxvt unicode, I have changed only the following line:

URxvt*color14: Cyan3

Tags: linux

cmus

November 20, 2013

If you want a music player that runs on the terminal, cmus is the way to go. Make sure you configure cmus to use software volume. You can configure software volume either through the configuration file or inside cmus. Inside cmus, press key 7 to go the configurations page and look for the configuration variable softvol. Make sure to set it to true to enable software volume.

Another way to achieve the same effect is to execute a command inside cmus. Execute the following command to enable software volume.

:set softvol=true

Tags: slackware

In Slackware, to get links that come in Thunderbird emails to open with Google Chrome instead of Firefox, just edit the mime types RDF schema.

.thunderbird/<profile_name>/mimeTypes.rdf

And, in the item containing the HTTP protocol handler, replace Firefox with Google Chrome.

<RDF:Description RDF:about="urn:scheme:externalApplication:http"
                 NC:prettyName="firefox"
                 NC:path="/usr/bin/firefox" /> 

For example,

<RDF:Description RDF:about="urn:scheme:externalApplication:http"
                 NC:prettyName="google-chrome"
                 NC:path="/opt/google/chrome/google-chrome" />

The above configuration opens HTTP links with Google Chrome. You might want also to open HTTPS links with Google Chrome. In order to do this, add a tag similar to the previous one but replacing "http" with "https". You can add this tag below the previous one.

<RDF:Description RDF:about="urn:scheme:externalApplication:https"
                 NC:prettyName="google-chrome"
                 NC:path="/opt/google/chrome/google-chrome" />

You would expect Thunderbird to work with the Freedesktop.org utils, such as, xdg-open. However, I have never managed to get this working.

This post is an adaptation from the post on Making Thunderbird Open Links in Chromium.

Tags: slackware

MPlayer with ALSA

May 29, 2013

If you're using multiple sound sources, for example, Youtube, Skype, and MPlayer, all at the same time, you are going to run into problems with MPlayer because, by default, MPlayer tries to use OSS. With OSS, having multiple sound sources playing simultaneously is not possible because they lock the special file /dev/dsp. On the other hand, instructing MPlayer to use ALSA on every command line is cumbersome. Instead, edit MPlayer's configuration file ~/.mplayer/config and add the following property

ao=alsa

Tags: linux