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
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
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
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
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