System daemons
As already stated, a daemon is a computer program that runs as a background process; in particular, for a Unix system, the Unix bible Advanced Programming in the UNIX Environment by Richard Stevens says:
Daemons are processes that live for a long time. They are often started when the system is bootstrapped and terminate only when the system is shutdown. We say they run in background, because they don't have a controlling terminal.
This behavior is so important that a special function has been implemented in the glibc library that permits the developer to easily create a daemon process. The function is (obviously) named daemon()
.
Just to fix this concept, we report a possible implementation of the daemon()
function in order to show you which steps a process should carry out in order to turn itself into a daemon:
int daemon(void) { int fd; /* Create the daemon grand-child process */ switch (fork()) { case -1: return -1; /* error! */ case 0: break; /* child continues... */ default: exit(0); /* parent goes... bye bye!! */ } /* This code is now executed by the shell's grand-child */ if (setsid() < 0) /* become a session leader */ return -1; if (chdir("/") < 0) /* change working directory */ return -1; umask(0); /* clear file mode creation mask */ /* In the end close all open file descriptors */ for (fd = sysconf(_SC_OPEN_MAX); fd > 0; fd--) close(fd); return 0; }
The first thing to do for a daemon candidate process is to call fork()
and then the exit()
system calls. This is because if the daemon is started as a simple shell command with the parent terminate makes, the shell thinks that the command is done and the prompt can be returned to the user. Then, the setsid()
call is needed to run the new daemon candidate process in a new session and have no controlling terminal.
The chdir()
system call is needed in order to avoid the daemon the candidate process is running on a mounted filesystem and then prevent it from be unmounted. In fact, the current working directory is inherited by the parent and changing it to the root (the slash character "/"
in the preceding code) is a trick to prevent this problem. The umask()
system call is then used to permit the newly created daemon from creating files with specific permissions without restrictions.
The last step closes all open file descriptors eventually inherited by the grandparent (the shell in this case). By closing all the process communication channels, the daemon cannot be managed by the user anymore; however, in order to make it possible to change some daemon functionalities, it may reopen a dedicated channel (usually a Unix domain socket) when receiving some configuration commands, or it can be designed in such a way that it rereads its configuration file when a special signal arrives.
Note
Details of how a daemon works or how it can be created are out of the scope of this book. You can take a look around the Internet starting with http://en.wikipedia.org/wiki/Daemon_%28computing%29 or (better) by reading the Unix bible, Advanced Programming in the UNIX Environment by Richard Stevens.
Useful and ready-to-use daemons
In a GNU/Linux system (and a Unix system in general), there exist a lot of ready-to-use daemons that are used to do real common tasks. The most notable ones are as follows:
- Apache, uhttpd, and lighttpd: The HTTP server daemons.
- atd and crond: The task scheduler daemons.
- ftpd and tftpd: The file transfer daemons.
- inetd and xinetd: The Internet super server daemons.
- named/bind and C: The DNS server daemons.
- nfsd, lockd, mountd, and statd: The NFS daemon and support daemons.
- ntpd: The NTP service daemon.
- portmap, rpcbind: The SunRPC port mappers.
- mysqld, postgresql, and C.: Database server daemons.
- sendmail, exim, postfix, and C.: The mail transfer agent daemons.
- snmpd: The Simple Network Management Protocol (SNMP) daemon.
- syslogd and C.: The system logging daemons.
- systemd: The system management daemon.
- telnetd and sshd/dropbear: Telnet and SSH server daemons.
- vsftpd and Co.: The File Transfer Protocol (FTP) server daemons.
Some of these have already been introduced in previous chapters due to the fact that they have been used in some examples, so we're going to add a little list of other useful daemons the developer may use to simplify their job with a brief explanation on how to use and how to get access to them using one of our developer kits.
For the other daemons, we encourage you to surf the Internet in order to know more about them; they may discover interesting thing.
System daemons management
Each Linux distribution has its own way to manage system daemons; in our systems, we're using Debian, so the way we have to use to manage our daemons is by calling the relative management script (which is placed in the /etc/init.d
directory) with proper option arguments. This way of operation is the legacy mode related to the initd
daemon, which is present in all Debian releases; however, in our embedded kits, we've installed a recent release that uses the systemd
daemon. This new daemon is backward-compatible with initd
, but it also introduces a new service management behavior.
Note
We have no space available to go deeply into what initd
and systemd
are and in what way they differ from each other, so you should start from the next two URLs to get further information on these important daemons: https://en.wikipedia.org/wiki/Systemd and https://en.wikipedia.org/wiki/Init .
In this book, we're going to use the legacy mode for two main reasons: the new way is present on latest releases only, while the legacy one can be used everywhere and because I still prefer using it; however, a brief note on the new behavior is reported for sake of completeness. So, as an example, let's start by taking a look at the /etc/init.d
directory in order to have a list of the available services:
root@bbb:~# ls /etc/init.d/ alsa-utils hostapd mysql sendsigs apache2 hostname.sh netscript single avahi-daemon hwclock.sh networking skeleton bootlogs killprocs pppd-dns ssh bootmisc.sh kmod procps sudo checkfs.sh loadcpufreq rc udev checkroot-bootclean.sh motd rc.local udhcpd checkroot.sh mountall-bootclean.sh rcS umountfs cpufrequtils mountall.sh README umountnfs.sh cron mountdevsubfs.sh reboot umountroot dbus mountkernfs.sh rmnologin urandom halt mountnfs-bootclean.sh rsync xinetd hdparm mountnfs.sh rsyslog
Tip
With the new behavior, we can use the following command:
root@bbb:~# service --status-all [ - ] alsa-utils [ + ] apache2 [ + ] avahi-daemon [ - ] bootlogs [ - ] bootmisc.sh [ - ] checkfs.sh ...
As we can see, there are a lot of available services; however, let's consider the Apache service and try to get its status, and then we have to execute the /etc/init.d/apache2
program with the status option argument, as follows:
root@bbb:~# /etc/init.d/apache2 status . apache2.service - LSB: Apache2 web server Loaded: loaded (/etc/init.d/apache2; generated; vendor preset: enab led) Active: active (running) since Mon 2016-10-10 12:01:10 UTC; 1 day 1 0h ago Docs: man:systemd-sysv-generator(8) Process: 3315 ExecReload=/etc/init.d/apache2 reload (code=exited, st atus=0/SUCCESS) Process: 1641 ExecStart=/etc/init.d/apache2 start (code=exited, stat us=0/SUCCESS) CGroup: /system.slice/apache2.service +-1972 /usr/sbin/apache2 -k start +-3371 /usr/sbin/apache2 -k start +-3372 /usr/sbin/apache2 -k start +-3373 /usr/sbin/apache2 -k start +-3374 /usr/sbin/apache2 -k start \-3375 /usr/sbin/apache2 -k start Oct 10 12:01:06 bbb systemd[1]: Starting LSB: Apache2 web server... Oct 10 12:01:10 bbb apache2[1641]: Starting web server: apache2. Oct 10 12:01:10 bbb systemd[1]: Started LSB: Apache2 web server. Oct 11 06:25:07 bbb systemd[1]: Reloading LSB: Apache2 web server. Oct 11 06:25:08 bbb apache2[3315]: Reloading web server: apache2. Oct 11 06:25:08 bbb systemd[1]: Reloaded LSB: Apache2 web server.
Tip
With the new behavior, we can use the following command to get the same output as earlier:
root@bbb:~# service apache2 stop
The service is in the active status, and if we wish to stop it, we can use the same preceding command but by specifying the stop option argument:
root@bbb:~# /etc/init.d/apache2 stop [ ok ] Stopping apache2 (via systemctl): apache2.service.
Note
With the new behavior, we can use the following command to stop the daemon:
root@bbb:~# service apache2 stop
We get no output by executing this command.
We can verify that the service is stopped by executing the status command again. Now, if we wish to restart the daemon, we can use the following command:
root@bbb:~# /etc/init.d/apache2 start [ ok ] Starting apache2 (via systemctl): apache2.service.
Note
With the new behavior, we can use the following command to stop the daemon:
root@bbb:~# service apache2 start
Again, we get no output by executing this command.
A useful trick to stop and start a daemon again-for example, after we've changed its configuration files-is using the restart
option argument as follows:
root@bbb:~# /etc/init.d/apache2 restart [ ok ] Restarting apache2 (via systemctl): apache2.service.
Note
With the new behavior, we can use the following command to stop the daemon:
root@bbb:~# service apache2 restart
There's still no output by executing this command.
These commands work in the same manner for all the system daemons we can find in our Debian OS, so we can use them to manage the following daemons too.
syslogd
When we talk about daemons, one of the most important ones is syslogd! The syslogd daemon is a widely used standard for message logging that permits the separation of the software that generates messages from the system that stores them and from the software that reports and analyzes them.
Due to the fact that a daemon has all the communication channels closed by default, this is the most efficient and easy method to report a daemon's activities to the system administrator/developer.
In the Debian system, we've installed into our developer kits at the beginning of this book; the syslogd service is implemented by the rsyslog package, which holds the rsyslogd
daemon. However, the scope of this book does not include a detailed explanation on how it works but just how it can be used to efficiently log some messages in order to keep track of our applications or just to debug them. In the next sections, we're going to see how it can be accessed using different programming languages but before we can start seeing how we can configure it in order to log a remote system (which can be very useful when we we work with embedded systems).
If we wish to send log messages from our BeagleBone Black to the host, we have to modify the rsyslog package's /etc/rsyslog.conf
configuration file in the host, as follows:
--- /etc/rsyslog.conf.orig 2017-01-14 22:24:59.800606283 +0100 +++ /etc/rsyslog.conf 2017-01-14 22:25:06.208600601 +0100 @@ -15,8 +15,8 @@ #module(load="immark") # provides --MARK-- message capability # provides UDP syslog reception -#module(load="imudp") -#input(type="imudp" port="514") +module(load="imudp") +input(type="imudp" port="514") # provides TCP syslog reception #module(load="imtcp")
This will enable the ability to receive log messages from a remote machine via UDP (we can use TCP too). Then, to enable the new configuration, we have to restart the daemon on the host using the next command, as explained earlier:
$ sudo /etc/init.d/rsyslog restart [ ok ] Restarting rsyslog (via systemctl): rsyslog.service.
Note
We need the sudo
command on the host since the system's daemons can be managed by the root user only.
Then, on the BeagleBone Black, we have to add the following line on the /etc/rsyslog.conf
file (usually at the end of the file):
*.* @192.168.7.1:514
In this manner, we ask to rsyslog
to send all log messages to the host at the IP address 192.168.7.1
on port 514
(where our host PC is listening). Again, in order to enable the new configuration, we have to restart the daemon with the same command used on the host, as the one shown here:
root@bbb:~# /etc/init.d/rsyslog restart [ ok ] Restarting rsyslog (via systemctl): rsyslog.service.
If everything works well, when we take a look at log messages on the host, we should see the ones from the BeagleBone Black, as reported here:
Jan 14 22:29:01 hulk ntpd[23220]: Soliciting pool server 193.234.225.2 37 Jan 14 22:29:20 hulk ntpd[23220]: Soliciting pool server 2a00:dcc0:dea d:b9ff:fede:feed:e39:73d7 Oct 11 22:55:05 bbb rsyslogd: [origin software="rsyslogd" swVersion="8 .4.2" x-pid="5540" x-info="http://www.rsyslog.com"] start Oct 11 22:55:04 bbb systemd[1]: Stopping System Logging Service... Oct 11 22:55:04 bbb systemd[1]: Stopped System Logging Service. Oct 11 22:55:04 bbb systemd[1]: Starting System Logging Service... Oct 11 22:55:05 bbb systemd[1]: Started System Logging Service. Oct 11 22:55:14 bbb rsyslogd: [origin software="rsyslogd" swVersion="8 .4.2" x-pid="5540" x-info="http://www.rsyslog.com"] exiting on signal 15.
Note
After the system date (which is wrong for the BeagleBone Black), we can see the system's name that is set as hulk
for the author's host PC and as bbb
for the BeagleBone Black.
syslogd in Bash
From the Bash shell, we can use the logger
command, as follows:
root@bbb:~# logger -t mydaemon logging message in bash
This command will generate the following message in the /var/log/syslog
file:
root@bbb:~# tail -f /var/log/syslog | grep mydaemon Apr 2 18:29:03 bbb mydaemon: logging message in bash
syslogd in C
The same message can be also generated in C language using the code in the chapter_04/syslogd/logger.c
file in the book's example code repository. The code simply calls three functions to do its job and this is the code snippet:
openlog("mydaemon", LOG_NOWAIT, LOG_USER); syslog(LOG_INFO, "logging message in C"); closelog();
Just compile and execute it using the following command lines:
root@bbb:~# make logger cc -Wall -O2 logger.c -o logger root@bbb:~# ./logger
Then, in the /var/log/syslog
file, we should get the following output:
Apr 2 18:33:11 bbb mydaemon: logging message in C
syslogd in PHP
In PHP, we can use the code in the chapter_04/syslogd/logger.php
file in the book's example code repository. Again, we just need three functions to do the job, and this is the code snippet:
openlog("mydaemon", LOG_NOWAIT, LOG_USER); syslog(LOG_INFO, "logging message in PHP"); closelog();
The example program can be executed with the following command:
root@bbb:~# php logger.php
Again, as earlier, we can see the generated message as follows:
Apr 2 18:43:52 bbb mydaemon: logging message in PHP
Note
The complete documentation for the syslog library routine is at: http://php.net/manual/en/function.syslog.php .
syslogd in Python
The last example is in Python, and it's stored in the chapter_04/syslogd/logger.py
file in the book's example code repository. We use the same three functions again:
syslog.openlog("mydaemon", syslog.LOG_NOWAIT, syslog.LOG_USER) syslog.syslog(syslog.LOG_INFO, "logging message in Python") syslog.closelog()
Then, we can execute it with the following command:
root@bbb:~# python logger.py
And, as earlier, it will generate the following message:
Apr 2 18:45:08 bbb mydaemon: logging message in Python
Note
The complete documentation for the syslog library routines is at: https://docs.python.org/3.4/library/syslog.html .
cron
This daemon is very useful to execute simple and repetitive tasks in the background; in fact, it executes scheduled shell commands according to a timetable called crontab, which the developer can use to program their tasks.
The crontab must be accessed and updated using the crontab
command, and in order to better explain how the cron daemon works, you should take a look at the current crontab of the root user with the following command:
root@bbb:~# crontab -e
Tip
It may happen that age:
/usr/bin/select-editor: 1: /usr/bin/select-editor: gettext: not found 'select-editor'. /usr/bin/select-editor: 1: /usr/bin/select-editor: gettext: not found 1. /bin/nano <---- 2. /usr/bin/vim.basic 3. /usr/bin/vim.tiny
/usr/bin/select-editor: 32: /usr/bin/select-editor: gettext: not found 1-3 [1]:
This is because we haven't chosen a default editor yet; however, we just need to select one in the list and the message will disappear.
When the preceding command is used, the embedded kit will open a text file using the current text editor, where the content is shown as follows:
# Edit this file to introduce tasks to be run by cron. # # Each task to run has to be defined through a single line # indicating with different fields when the task will be run # and what command to run for the task # # To define the time you can provide concrete values for # minute (m), hour (h), day of month (dom), month (mon), # and day of week (dow) or use '*' in these fields (for 'any').# # Notice that tasks will be started based on the cron's system # daemon's notion of time and timezones. # # Output of the crontab jobs (including errors) is sent through # email to the user the crontab file belongs to (unless redirected). # # For example, you can run a backup of all your user accounts # at 5 a.m every week with: # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ # # For more information see the manual pages of crontab(5) and cron(8) # # m h dom mon dow command
Tip
Note that the default editor can be changed by setting the EDITOR
environment variable as follows:
root@bbb:~# export EDITOR=nano
Then, the BeagleBone Black will use the nano
command to show the file holding crontab.
Just reading the comments into the crontab file, it's quite easy to understand how the daemon works: we have one task per line and the first five fields of each line define at which instant the command in the sixth field must be executed. For example, as reported in the earlier comments, in order to run a backup of all BeagleBone Black's user accounts at 5 a.m. every week, the schedule line should be as follows:
0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
The first five fields do the trick; in fact, the first field tells cron that the command must be run at minute (m) 0
, the second set the execution hour (h) at 5
(hours are from 0 to 23), the third and the fourth fields, using the wildcard *
character, say respectively that the command must be executed each day of month (dom) and each month (mon), while the fifth says that the command must be executed on the day of week (dow) 1
, that is on Monday (numbers 0 or 7 is for Sunday).
Another useful feature is that in the crontab file, the developer can also set some variables to modify the default behavior-for example, the default value for the PATH
variable is "/usr/bin:/bin"
and we can modify it to add the user's bin directory using the following line:
PATH=~/bin:/usr/bin/:/bin
Note that the ~
character is correctly interpreted by the shell (which is set to SHELL=/bin/bash
by default), while the same is not valid for the environmental substitutions or replacement of variables, thus lines such as the following will not work as you might expect; that is, there will not be any substitution:
PATH = $HOME/bin:$PATH
Note
You can get more information by reading the crontab file's man pages using the man
command:
root@bbb:~# man 5 crontab
xinetd
This tool is a network daemon program that specializes in adding networking features to programs that normally do not not have it (we already saw this daemon for the host in Chapter 2 , Managing the System Console, in Loading files from the network section). This daemon is an enhanced version of the standard inetd daemon, but nowadays, it replaces inetd in most distributions.
The xinetd
configuration file is /etc/xinetd.conf
, which usually looks like the following:
# Simple configuration file for xinetd # # Some defaults, and include /etc/xinetd.d/ defaults { # Please note that you need a log_type line to be able to use # log_on_success # and log_on_failure. The default is the following : # log_type = SYSLOG daemon info } includedir /etc/xinetd.d
So the real configuration settings are in the /etc/xinetd.d
directory, which in turn holds one file per service. In our BeagleBone Black, we have the following listing:
root@bbb:~# ls /etc/xinetd.d/ chargen daytime discard echo time
Each configuration file tells the daemon what program needs to be run when an incoming network connection is received, but before doing it, it redirects the program's stdin
, stdout
, and stderr
streams to the socket used to manage the connection. By doing this, every program that simply writes and reads data to and from the standard Unix streams can talk remotely over a network connection!
Let's look at a simple example and consider the following Bash script:
/bin/bash while /bin/true; do read line line=$(echo $line | tr -d '\n\r') [ "$line" == "quit" ] && break; echo -e "$line\r" done exit 0
Note
The code is hold in the chapter_04/xinetd/echo.sh
file in the book's example code repository.
If we try to run it, we get this:
root@bbb:~# ./echo.sh
Now if try to enter the Testing request
string, the script will echo it on its stdout
(that is, on the terminal window). Then, in order to exit the program, we must enter the quit
string. Here's a simple usage:
root@bbb:~# ./echo.sh Testing request Testing request quit root@bbb:~#
Now if we add the following code held in the chapter_04/xinetd/echo_sh
file in the book's example code repository in the /etc/xinetd.d
directory, we can test the xinetd
functionality:
service at-echo { disable = no socket_type = stream protocol = tcp wait = no user = root server = /root/echo.sh }
Using the preceding code, we define a new service named at-echo
defined in the /etc/services
file, as follows:
root@bbb:~# grep at-echo /etc/services at-echo 204/tcp # AppleTalk echo at-echo 204/udp
Then, we specify the TCP protocol and the program to execute when a new connection is established; in our case, we execute /root/echo.sh
as the user root when a new TCP connection at port 204
is done. The /root/echo.sh
program simply reads a line from stdin
and then writes it back to the stdout
stream.
Now we must restart the daemon to activate the new settings:
root@bbb:~# /etc/init.d/xinetd restart [ ok ] Restarting xinetd (via systemctl): xinetd.service.
As a first step, we can verify that the daemon is really listening on port 204
, as expected:
root@bbb:~# netstat -lpn | grep 204 tcp 0 0 0.0.0.0:204 0.0.0.0:* LISTEN 2724/xinetd
We can check whether our settings are OK and even looking at the system's logging messages in the /var/log/syslog
file, as follows:
root@bbb:~# tail -f /var/log/syslog Apr 2 20:28:29 bbb xinetd[2655]: Starting internet superserver: xinet d. Apr 2 20:28:29 bbb systemd[1]: Started LSB: Starts or stops the xinet d daemon.. Apr 2 20:28:30 bbb xinetd[2664]: Reading included configuration file: /etc/xine td.d/chargen [file=/etc/xinetd.conf] [line=14] ... Apr 2 20:28:30 bbb xinetd[2664]: Reading included configuration file: /etc/xine td.d/echo_sh [file=/etc/xinetd.d/echo_sh] [line=26] Apr 2 20:28:30 bbb xinetd[2664]: Reading included configuration file: /etc/xine td.d/time [file=/etc/xinetd.d/time] [line=9] Apr 2 20:28:30 bbb xinetd[2664]: removing chargen Apr 2 20:28:30 bbb xinetd[2664]: removing chargen ... Apr 2 20:28:30 bbb xinetd[2664]: removing time Apr 2 20:28:30 bbb xinetd[2664]: removing time Apr 2 20:28:30 bbb xinetd[2664]: xinetd Version 2.3.15 started with l ibwrap loa davg options compiled in. Apr 2 20:28:30 bbb xinetd[2664]: Started working: 1 available service
All configuration files are parsed and then only not disabled services are kept so, in the end, only our new service is up and running!
Now we can test our new network service from the host PC using the telnet program, as follows:
$ telnet 192.168.7.2 204 Trying 192.168.7.2... Connected to 192.168.7.2. Escape character is '^]'. Testing request Testing request quit Connection closed by foreign host.
This time, we execute the echo.sh
script again but using a remote TCP connection!
Note
The telnet
program has been installed in the preceding section with the xinetd daemon.
sshd
This daemon implements the secure shell service that allows us to use a computer's terminal from a remote machine using an encrypted protocol. This daemon is widely used and is very famous, so it doesn't need any presentation or usage examples; however, in this book, we're going to use it in several different ways:
- To copy files to or from a remote machine
- To execute a remote command
- With the
X11Forwarding
ability
Copying files to or from a remote machine is simple, and the command to be used is scp
in the following form:
root@bbb:~# scp local_file giometti@192.168.7.1:/tmp/
The command copies the local_file file to the remote machine's /tmp
directory at the address 192.168.7.1
. For further scp
usage forms, you should take a look at the relative man pages.
Executing a remote command is a useful behavior we can use to get on the local machine the output of a command executed on a remote one (actually, we can also manage the program's input). As a simple example, the following command executes the ls
command on the BeagleBone Black from the host PC and then displays the result on the host's terminal:
$ ssh root@192.168.7.2 ls /etc/init.d root@192.168.7.2's password: alsa-utils apache2 avahi-daemon ...
After entering the root's password, we get the BeagleBone Black's /etc/init.d
directory content. Again, for further information, the man pages are your best friends.
The last usage we wish to present here (and that will be used in the following chapter) is the X11Forwarding
ability, that is, the possibility to execute an X11
application on a remote machine and then see its window on the local machine. Strictly speaking, this behavior can be considered an extended form of the remote commands execution we saw earlier.
Note
In this book, we cannot explain in detail what the X11
protocol is; so, you should consider taking a look at the next URL for further information: https://en.wikipedia.org/wiki/X_Window_System.
As a simple example, let's try to execute the xcalc
graphical application on the BeagleBone Black from the host PC and then display its window on the host's display. First of all, we have to install the xcalc
application on the BeagleBone Black with the xauth
utility:
root@bbb:~# apt-get install x11-apps xauth
Then, we have to execute the xauth
utility in order to build up the required configuration files:
root@bbb:~# xauth xauth: file /root/.Xauthority does not exist Using authority file /root/.Xauthority xauth>
Then, we can use the quit
command to close the program and proceed to enable the X11Forwarding
ability for the sshd
daemon. This can be done by setting the X11Forwarding
option to yes
in the /etc/ssh/sshd_config
file, as shown here:
root@bbb:~# grep X11Forwarding /etc/ssh/sshd_config X11Forwarding yes
In this case, the option is already enabled, but in case it is not, we have to enable it and then we must restart the daemon in the usual manner in order to enable the new configuration.
Now we're ready; on the host, we can use the following command to log in to the BeagleBone Black:
$ ssh -X root@192.168.7.2
In the preceding command, the -X
option argument enables the X11Forwarding
, as stated in the ssh
man pages. Now we can safely ignore the /usr/bin/xauth: file /root/.Xauthority does not exist
warning message and can execute the xcalc
application normally:
root@bbb:~# xcalc
If everything works well, the xcalc
main window will appear on the host display.
Apache
The Apache HTTP server is (maybe) the most famous and used web server software in the world. It's installed in (almost) every distribution by default, and it can be used for tons of different tasks related to the World Wide Web.
We cannot report all possible configuration settings available for the Apache server here since we'll need a whole book for that; however, we're going to report some settings we're going to use later on in this book.
First of all, we have to verify that the server is up and running in our embedded system, so let's open the web browser in the host PC and point it to the internal IP address 192.168.7.2
; if everything works well, we should get something similar to what's shown in the following figure:
Now we need to check the PHP support, that is, the ability of the Apache server to execute PHP code. To do that, we have to create a index.php
file, as follows, and then put it in the /var/www/html
directory:
<?php phpinfo(); ?>
Then, if we point our web browser to the http://192.168.7.2/index.php
URL, we should get the following output:.
Note
Note that the PHP support for Apache has been installed in the earlier section, where we've set up the system. What we've done is just one of the several possibilities the Apache web server offers, and you can read more on The Apache HTTP Server Project at: https://httpd.apache.org/ .
MySQL
Usually, we consider this daemon to be used on large servers, but it can be efficiently used in an embedded system too! For example, it can be used to implement a common configuration system or a status system where more processes can get/set the configuration data and/or status data. Or, it can be used efficiently to log several events and/or environment data collected from the sensors.
The daemon should already be set up and running, so now, we can see several ways to get access to its internals. From Bash, we can use the mysql
command, as shown here:
root@bbb:~# mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 47 Server version: 5.5.47-0+deb8u1 (Debian) Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
Note
When the BeagleBone Black asks for a password, we should just use the one we set up earlier during the daemon installation.
MySQL in Bash
To use MySQL efficiently, we should create a custom database and then use it to do our job. As an example, we can use the script in the chapter_04/mysql/my_init.sh
file in the book's example code repository to generate a custom database called sproject
.
The code is quite simple; after a warning message, we use the <<__EOF__
trick to pass a script from the command line to the mysql
tool.
Note
An example of the <<__EOF__
trick we refer to is reported as follows:
mysql -u root -p <<__EOF__
COMMAND 1
COMMAND 2
...
COMMAND n
__EOF__
This trick is often used when we need to supply one or more commands to a program directly in its standard input line (stdin
). Using this syntax, we tell the Bash shell to send the lines between the command itself and the line holding the __EOF__
characters directly into the stdin
of the executed command.
The script first recreates a new database (eventually deleting all the existing data) and then adds a new status
table that we can use to store a system's status data. Here is the code snippet:
# Drop all existing data!!! DROP DATABASE IF EXISTS sproject; # Create new database CREATE DATABASE sproject; # Grant privileges GRANT USAGE ON *.* TO user@localhost IDENTIFIED BY 'userpass'; GRANT ALL PRIVILEGES ON sproject.* TO user@localhost; FLUSH PRIVILEGES; # Select database USE sproject; # Create the statuses table CREATE TABLE status ( t DATETIME NOT NULL, n VARCHAR(64) NOT NULL, v VARCHAR(64) NOT NULL, PRIMARY KEY (n), INDEX (n) ) ENGINE=MEMORY;
Note that the table has been created using the MEMORY
engine. This engine uses the system's memory to store the information instead of using the mass memory devices (that is, hard disks, microSD cards, and so on). This trick allows us to execute very quick queries to the database, but it can be used where the data is dynamically recreated each time our system restarts due to the fact that they vanish at the system reboot (also, we must consider that the maximum size of the database is limited by the amount of installed memory).
At this point, we can add some entries using the code in the chapter_04/mysql/my_set.sh
file in the book's example code repository. We can use it with the following command line:
root@bbb:~# ./my_set.sh T1 23.5
The script uses the SQL REPLACE
command to do the job. The code snippet is just a line of code:
REPLACE INTO status (t, n, v) VALUES(now(), '$name', '$value');
Now, to verify that the data is correctly collected in the database, we can do a simple dump of the status
table created earlier using the my_init.sh
file. Then, we use the following command to dump all data in the table:
root@bbb:~# ./my_dump.sh t n v 2016-04-02 18:25:35 T1 23.5
In this case, all the job is done using the SQL SELECT
command. Again, the code snippet is just a line of code:
SELECT * FROM status;
Note
A complete guide to the MySQL internals and SQL language can be found on the MySQL documentation site at: https://dev.mysql.com/doc/ .
The real power of MySQL is that the preceding actions can be done in different languages, and just to give you some useful hints you can take to start developing your controlling/monitoring system with the BeagleBone Black, we're going to show you how to get access to the sproject
database from the C, PHP, and Python languages.
Note
In the next example, we're not going to rewrite the my_init.sh
script in different languages since it can be deduced from the other examples, and in any case, it is not a significant example indeed. It's just creating the database, and once used, it is not useful anymore.
MySQL in C
In C language, the my_set
script can be implemented as reported in the chapter_04/mysql/my_set.c
file in the book's example code repository. The code is quite similar to the Bash one even if it's a bit complex; however, the important parts are the tree calls to the mysql_init()
, mysql_real_connect()
, and mysql_query()
functions. The first two just initiate the connection, while the third executes the query. Here is the code snippet:
/* Get connect to MySQL daemon */ c = mysql_init(NULL); if (!c) { fprintf(stderr, "unable to init MySQL data struct\n"); return -1; } if (!mysql_real_connect(c, "127.0.0.1", "user", "userpass", "sproject", 0, NULL, 0)) { fprintf(stderr, "unable to connect to MySQL daemon\n"); ret = -1; goto close_db; } /* Ok, do the job! */ ret = asprintf(&sql, query, name, value); if (ret < 0) { fprintf(stderr, "unable to allocate memory for query\n"); goto close_db; } ret = mysql_query(c, sql); if (ret < 0) fprintf(stderr, "unable to access the database\n");
To complete our panoramic, we just have to show you how you can retrieve data from the MySQL daemon; to do that, we just need a simple implementation of my_dump
as in the chapter_04/mysql/my_dump.c
file in the book's example code repository. Note that in this case, the first three steps are quite similar to the my_set
case, but now, we have to manage an answer from the MySQL daemon too! To do that, we use the mysql_store_result()
function, which stores the received data in the q_res
variable, and then, using the mysql_fetch_field()
, mysql_num_fields()
, and mysql_fetch_row()
functions, we can extract the needed information. The code snippet for the relevant part is as follows:
/* Do the dump of the fields' names */ while ((field = mysql_fetch_field(q_res))) printf("%s\t", field->name); printf("\n"); /* Do the dump one line at time */ n = mysql_num_fields(q_res); while ((row = mysql_fetch_row(q_res))) { for (i = 0; i < n; i++) printf("%s\t", row[i] ? row[i] : NULL); printf("\n"); } mysql_free_result(q_res);
Well, now we are ready to compile the preceding programs using make
:
root@bbb:~# make cc -Wall -O2 -D_GNU_SOURCE -I/usr/include/mysql my_set.c -lmysqlcli ent -o my_set cc -Wall -O2 -D_GNU_SOURCE -I/usr/include/mysql my_dump.c -lmysqlcl ient -o my_dump
Note
By default, the libraries needed to compile this C program are not installed; however we did this in the earlier section, where we set up the system.
Now we can use them as we did earlier with Bash:
root@bbb:~# ./my_set T1 20 root@bbb:~# ./my_dump t n v 2016-04-02 18:36:19 T1 20
Note
A complete guide to the MySQL C API can be found at: http://dev.mysql.com/doc/refman/5.7/en/c-api.html .
MySQL in PHP
Now it's PHP's turn, and the my_set
program is in the chapter_04/mysql/my_set.php
file in the book's example code repository. In this case, the code is more compact than in C, but it looks like very similar: we still have a connection stage and then a query execution stage. The involved functions are now mysql_connect()
, mysql_select_db()
, and mysql_query()
. The relevant code is reported in the following snippet:
# Get connect to MySQL daemon $ret = mysql_connect("127.0.0.1", "user", "userpass"); if (!$ret) die("unable to connect with MySQL daemon"); $ret = mysql_select_db("sproject"); if (!$ret) die("unable to select database"); # Ok, do the job! $query = "REPLACE INTO status (t, n, v) " . "VALUES(now(), '$name', '$value');"; $dbres = mysql_query($query); if (!$dbres) die("unable to execute the query");
As in C, the PHP version of my_dump
has to manage the answer from the MySQL daemon and the code is in the chapter_04/mysql/my_dump.php
file in the book's example code repository. Even in this case, after the query, we get some data back, which we can extract using the mysql_num_fields()
, mysql_field_name()
, and mysql_fetch_array()
functions. Here is the code snippet:
# Do the dump of the fields' names $n = mysql_num_fields($dbres); for ($i = 0; $i < $n; $i++) printf("%s\t", mysql_field_name($dbres, $i)); printf("\n"); # Do the dump one line at time while ($row = mysql_fetch_array($dbres)) { for ($i = 0; $i < $n; $i++) printf("%s\t", $row[$i]); printf("\n"); }
Note
These functions are not supported by the basic PHP language and we need some external libraries that are not installed by default; we did this in the earlier section, where we set up the system.
These programs can now be used as the other programs, as follows:
root@bbb:~# ./my_set.php T1 19.5 root@bbb:~# ./my_dump.php t n v 2016-04-02 18:42:29 T1 19.5
Tip
A complete guide to the MySQL PHP API can be found at: http://php.net/manual/it/book.mysql.php .
MySQL in Python
In Python, the my_set
program can be as in the chapter_04/mysql/my_set.py
file in the book's example code repository. The program looks a bit different from the previous ones due the usage of the cursor; however, looking carefully at the code, we can see that there are very few differences. The MySQLdb.connect()
method does the connection with the MySQL daemon, and the execute()
method just executes the query. The following is the code snippet:
# Get connect to MySQL daemon db = MySQLdb.connect(host = "localhost", user = "user", passwd = "userpass", db = "sproject") # Create the Cursor object to execute all queries c = db.cursor() # Ok, do the job! query = "REPLACE INTO status (t, n, v) " \ "VALUES(now(), '%s', '%s');" % (sys.argv[1], sys.argv[2]) c.execute(query)
Regarding my_dump
, it can be as reported in the chapter_04/mysql/my_dump.py
file in the book's example code repository. This time, to retrieve the query's data, we use the fetchall()
method, and to get the headers, we use the description
attribute. The relevant code is reported in the following snippet:
# Save the query result data = c.fetchall() # Do the dump of the fields' names for field in c.description: print("%s\t" % (field[0])), print # Do the dump one line at time n = len(c.description) for row in data: for i in range(0, n): print("%s\t" % (row[i])), print
Note
The external library needed to execute these programs is not installed by default; however, we did this in the earlier section, where we set up the system.
In the end, we can test these programs using the following commands:
root@bbb:~# ./my_set.py T1 18 root@bbb:~# ./my_dump.py t n v 2016-04-02 18:49:43 T1 18
Tip
The complete MySQLdb User's Guide is reported at: http://mysql-python.sourceforge.net/MySQLdb.html .