CHAPTER 7

image

Managing Your Pi

So now you have this magical Pi, installed Raspbian, logged in and got your Pi the way you like it, and even have a few things in mind to do with it. Although you are becoming familiar with Linux, the problem is that every time your Pi is switched off or loses power, you need to go over to it, log in and physically set things up before you can use it again. Well, no more!

This chapter is about systems administration and some of the basic application functions that will allow you to manage the way your system functions at boot. To help, we will also give you a crash course in coding with the BASH language so that you can create start scripts of your own to initiate applications (we’ll cover just what a script is, too). Finally, we will cover some basic security for your system, including user management, so you can change your password and add new users to your system.

Remotely Accessing the Pi

Probably one of the hardest things to get used to when working in a Linux environment when coming from a Windows or an OSX environment is working in the shell. For starters, it’s not pretty, but more importantly humans are creatures who are very much used to manipulating things to achieve an outcome. This means that a GUI-based environment is much more intuitive—at first. Having come from a Windows environment, I can say for certain that the command line is such a great environment to work in. The amount you can do with a few deft keystrokes—compared with keyboard and mouse work, clicking, dragging, and performing context actions is amazing. Being able to work within a system purely from the command line is a great asset indeed. But you need to become familiar with it, and the only way to do this is practice!

Why am I talking about the command line when we need to talk about managing your Pi? Because most Linux management is achieved through the command line, with most if not all of the system functions you want to use being available from the command line. Moreover, it is far easier and less resource-intensive to access your system via the command line than it is to access it via the GUI. To access your system remotely via the command line, you simply need to use Secure Shell (or SSH for short), which you first saw in Chapter 3. You also need to ensure that your system’s network access is available on boot as well. These two functions serve the basics of being able to manage your system from anywhere as you have network enabled and then can use SSH to connect to your system. So, let’s examine these two functions a little more closely so that you understand how to ensure you can always access your Pi to manage it.

Networking

If you are like most people and have a router to provide multiple devices on your system with network access, you will probably be aware that your router automatically provides network IP addresses to all your devices. It doesn’t do this through any sort of magic, but rather through a special protocol called DHCP, which stands for Dynamic Host Control Protocol. The purpose of DHCP is to automatically assign an IP address to a machine, which means there is no need to manually assign it one. DHCP is invoked automatically by the network service when it is configured to do so. We cover how to configure networking in much more detail within Chapter 9 WiPi.

However, if for some reason you need to manually invoke DHCP to assign you an IP address and get you connected to the network, you need to use the dhclient command. You will need to run it as root, as you will need to do with most of these commands, because you are changing core system functionality. If you just run dhclient as is, you will try and get a new IP address for every interface on the system, which may not be desirable. You can actually specify a specific interface for dhclient to work with by simply adding it as the first argument of the command (i.e., dhclient eth0).

DNS

Probably one of the most important things to keep in mind when working with your systems networking is DNS, which is short for Domain Name System. DNS is the way that every system on the Internet is able to turn URLs (e.g., www.apress.com) into an IP address (e.g., 173.203.147.183). DNS on your Linux machine works by knowing where it should direct its queries. This is governed by the file /etc/resolv.conf, which contains a name server directive that tells your system where the name server it should query is located. Normally this is your router, but it can be an Internet DNS server such as Google’s 8.8.8.8. These will appear in resolv.conf like this:

nameserver 10.0.0.1

SSH

SSH is a way that you can remotely gain a secure and encrypted connection to your Pi’s shell without needing to physically do anything to it. Getting SSH installed and running, along with basic commands to access your system, are covered in Chapter 3 so we won’t go into much detail and will instead be diving deep into the function of SSH. SSH is provided by the sshd daemon that is run at boot time on your Pi. You can start and stop this command in the same way that your system does, which is to use the sshd init script. All system init scripts are normally located in /etc/init.d/ and they are normally run as root. To use them, add the action you want to take (start, stop, and restart) to the first argument of the script. So to restart SSH, run /etc/init.d/ssh restart. Additionally, these scripts are the ones your system uses to run programs or commands on boot. So let’s have a look at how we can leverage this to write your own init scripts.

BASH: Basic Coding

Besides “Hey can you fix my computer?” the most common computing question I am asked is “Can you teach me how to write this application in C?”. Not a month goes by without someone I am working with, a friend, or a relative comes and asks me to teach them how to write software. Normally, they ask for a quick rundown of how to write applications in C. This is not an unreasonable question for the most part, but often when I ask them what they want to do, they want to do something simple. And while I would happily advocate learning C, there is a much simpler way for them to get introduced to the wonderful world of software design, without taking on the task of learning C (which for what they are normally after is a lot of overkill).

That is the goal of this part of the chapter. We aim to introduce you to coding, to basic logic structures, and to some of the basic software design principles so that you can get the job done. We will achieve this by introducing you to BASH. While many people will scoff, BASH is a perfect way to introduce the core fundamentals of what programming is about. BASH is also one of the most widely used languages for software in the world because most applications on Linux systems will have some level of BASH software performing some kind of intermediary functions somewhere along the route.

What Is BASH?

As covered in Chapter 3, BASH is short for Bourne Again Shell and is the default shell environment for most Linux and UNIX environments. When you log in to your system via the command line and are presented with your prompt, this prompt is provided to you by the BASH shell. The shell will take commands and instructions and process them to perform system functions. Most of the commands you normally issue when working with or administering your system via the command line are ones to invoke other applications so that you can generate output.

While this seems like a very obvious and simple way to go about things, it is also a fantastic way of demonstrating the core precept of how any software application works. You take a given input, perform some kind of computation upon it, a response (called the output) is generated. This process is, in fact, the basic process of so many functions of life and work that many people take for granted. It is this basic model of input, compute, output that will form the basic model of how we go about understanding how programming functions.

So back to the first question: what is BASH? BASH is a shell, and a shell is a method for taking inputs and computing against them to generate an output. BASH has a number of tools that allow you to use the same basic logic constructs of just about all programming languages. This is how BASH works as a programming language as well as a shell—you can write a number of these logical operations and commands and then use them to form a computation. Now you have a rudimentary understanding of what BASH is, what a shell is, what the broad purpose of any computer program is, and how all these functions are available to perform the basic action of input, compute, and output. Now that you have a very broad overview of what we want to achieve, we can use this knowledge as the starting block for learning how to write a program with BASH.

Starting in BASH

So you know what BASH is and what a shell is and how the rudimentary logic of a program works that’s often considered by many to be head and shoulders above the pack. In fact, several interviewers commented that while their employers had rigorous questioning of potential software engineers who could all espouse what we have covered, they complained that many employees and graduates could not form the logic for a basic program on a piece of paper. They went forward to propose a coding test, a very simple test that any software engineer should be able to solve quickly. This test, which is called the FizzBuzz test, is based on a children’s game of the same name. The basic principle is that you must count from 1 to 100 and say Fizz for every number that is a multiple of 3, you must say Buzz for every number that is a multiple of 5, and finally you must say FizzBuzz for every number that is a multiple of both 3 and 5. It sounds simple; that’s because it is. After all, most coding is simply building small blocks of logic and attaching them to other blocks of logic; mashing together lots and lots of little input, compute, and output nodes to form a larger computing system that does a much larger input, compute, and output process.

To this end, we will start by writing our own solution to the FizzBuzz problem in BASH so that you all can pass the basic software engineer competency test! That’s right; we will be programming in BASH. The BASH interpreter provides commands that have the same function as common programmatic logic elements. These can be combined with normal shell commands to form whole programs. So, let’s start writing (we covered text editors in Chapter 6, so you can use any you like). All you need is to be able to edit a file and execute it on the command line afterward. Go ahead and open up your favorite text editor and write the following:

#!/bin/bash

This is the first thing that anyone wanting to write a program in BASH should write. This is called the shebang, and it is a special symbol that when placed on the first line of any file means that it is the path to the script interpreting engine we are to use. In this case, we will be using the interpreter for BASH that is located in /bin/bash on your Pi and on just about every other Linux and UNIX operating system. This will tell the outside command prompt shell that when it is running this program, it should use the /bin/bash interpreter to execute it. You should always include some form of shebang at the start of your given program for any interpreted language.

Interpreted versus Compiled

This brings me nicely to the next point of order: interpreted vs. compiled languages. For these purposes, you won’t need to have a deep knowledge of compiled languages, but it is always good to know that they exist and to be aware of some of the differences between an interpreted language and a compiled language. So while both sets of languages allow you to write your own programs for the computer to execute, they go about this in fundamentally different ways.

Interpreted languages (sometimes called scripting languages) write down a series of programmatic commands that are fed to an interpreting engine (as given by the shebang, for example) that will extrapolate their computational meaning and perform the functions that the program intended. This is the pen-and-paper equivalent of a recipe for a cake, whereby you write down the exact method for how to make the cake, and the interpreter cooks it for you and outputs tasty delicious cake.

Image Note  This recipe nature is also much like a script for a play in that as long as you have the script and can read it you will perform the same actions each time. This is why interpreted languages are often known as scripting languages: their ultimate output is a collection of text that can be used to create the programmatic output the same way each time.

Compiled languages take a different approach. They also have a recipe (called source code), but instead of the recipe running, it will be taken to a specialized piece of software called a compiler that will take the recipe and create an executable package (called a binary) from it. This binary package is written in the computer’s own machine language so when executed it performs all the actions itself. This is the equivalent of writing the plans for a specialized cake-making system and then installing it in your system and having it make the cake. In both cases you get cake, but the interpreted one may take a little longer to make from the moment you say go. The flip side of this is that creating the specialized cake-making machinery takes much more effort. Now you should understand why I said languages like C were overkill for small problems—C is a compiled language.

Output in BASH

Back to our application: if you were to save and run your program, it would not do anything for you. So the first order of business should be to make your program output something. This is accomplished with the echo statement, which is also available from your command prompt shell; you use echo to make the system output a given set of text. So let’s add an output to your script to have it say hello to everyone out there in the big wide world. Modify your script so it looks like this:

#!/bin/bash
echo "Hello World!"

Now go ahead and execute your script; to do this, you will need to change the script’s file permissions to allow it to be executed. My script is called fizzbuzz.sh, so the commands needed to make it executable and then to execute it are these:

$ chmod +x fizzbuzz.sh
$ ./fizzbuzz.sh

This code will generate the following output:

Hello World!

Congratulations! You have just written the world’s most simple program, the Hello World program, which is the first program most software engineering students are given to create. It shows how to begin building a small piece of software and accomplishes the basic task of any software program: it generates output. In this case, the chain was that we gave it an input of “Please output ‘Hello World!’” and the system computed that and then gave us the desired output: Hello World! I wrapped Hello World! in a pair of quotation marks. (This is done so that the line of Hello World! is treated as a string of characters instead of other commands to execute.) All languages need to make the distinction between what is a piece of data and what is an actual piece of programmatic logic. The quotation marks in this instance show that everything within them is considered to be a string of text.

You may ask, “So what if I want to output a quotation mark? Suppose I want to output "Hello World!" like that in quotation marks.” Well, not a problem, we have you covered! All systems have the concept of what is called an escape. An escape is a special character that tells the interpreter or compiler to ignore the special properties of the next character and just treat it as part of a given string. In most systems, the escape character is a backslash (). So if we wanted to make our Hello World! have quotation marks, we would modify it to be as follows:

#!/bin/bash
echo ""Hello World!""

This shows that we want to escape those inner pair of quotation marks, so if we execute, we should see the following:

$ ./fizzbuzz.sh
"Hello World!"

Fantastic! You can even escape an escape if you want to print a backslash as the output of something in your code. Remember this as you are writing software because this is one of the most common mistakes people make—having unescaped characters in their code, which causes the rest of their software to have issues because the strings are mixed up.

Image Note  Probably the most commonly escaped character is , which is short for newline. This can be used to output text on the next line.

As an aside, this is where text editors that have syntax highlighting come into a realm of their own because they are an immense help in displaying which characters are part of a string and which aren’t. Vim has syntax highlighting, as does nano (but not over SSH); unfortunately, the default GUI editor LeafPad doesn’t, but there are a number of editors out there that do you simply need to find one that suits you. They also have other functions such as bracket matching and brace matching, but they won’t help us much at this stage.

Recap

So what have we learned so far? We have covered how to start off a BASH application using the shebang so we can pick out the right interpreter. You learned how to print basic output to the screen and also how to use the escape character within a string. This brings us nicely to the next point on our list: variables.

Variables

Variables are an abstract representation of a given piece of data. Come again? A variable is a way that a computer can store a given piece on information so that it can be retrieved again. This is the way that most information within a program is stored and manipulated because most programs do not work like our simple Hello World! They take various forms of information and turn it into other information. So let’s depart from our first example and return to the problem of writing a FizzBuzz application. We need to start by counting from 1 to 100.

We could simply write each of the numbers, one per line, and then perform the FizzBuzz calculations on each, but that’s no really a good and logical way of going about writing this program. What we need is an abstract variable that can contain the “number”; we can then run the FizzBuzz tests against this “number” and then we will simply increase the “number” by one and rinse and repeat until the number 100 has been tested. Then we are done!

So now you should have some understanding of what a variable is. It’s a way that we can represent any single piece of information. We don’t have to have a specific piece of information because the variable is just that—variable in nature? However, there are a few limitations of variables to consider. First, there are many different types of variables defined in the wider world of programming, such as Boolean, integer, double precision integer, long integer, character, string, floating point integer, vector, array, and so on. For BASH, there are only a few variable types, but we should be aware of the most important of the other types of variables because this can come into play with other programming languages:

  • First is the integer, which is a numeric variable that is designed to represent only a number (and in most cases only a whole number). This type is defined to allow easy use of mathematical and other numeric functions such as addition, subtraction, multiplication, and so on.
  • Second is the string, which is a type of written data and represents a long stream of written characters. Strings are normally related to user input and output because they are far too cumbersome to work with inside an application due to the highly variant nature of what can be included in a string of text.
  • Third is the array, which is a special type of variable because it is a metavariable and is basically a carrier for a number of other variables. The subvariables within an array are called elements and are referenced by adding a pair of square brackets to the end of the array variable and a number that corresponds to the element of the array in question. Arrays do not start at 1; they instead start at 0 because 0 is logically the first number in the counting sequence. So if you want to retrieve the fourth element from an array you would be adding [3] to the end of your array variable.

So now that you know what a variable is, we can start to form our basic program logic for solving the FizzBuzz problem.

Logical Operation: if

Now we have both the ability to output something and to store something, which covers the first and last portions of what one needs to do with a computer program input and output. What we need to do now is to actually perform some calculations on our variables so that we can have our program do something meaningful. This is where we break out into logical operations. In programming there are two basic logical operations that you will need to become intimate with. These two logical operations form the basis of most of the programming that will be done anywhere. They do this because they allow people to perform tests and take divergent actions depending on the outcome of the test.

The first of these statements is the if statement. An if statement performs just as its name implies—if something is true, an action is performed. It is the test nature of an if statement that provides it with all its power because when combined with the use of variables we can test just about anything we want. So looking at the FizzBuzz example, we know we have our initial variable “number” that will represent the counting numbers between 1 and 100. We will need to perform tests on “number” to see if it is divisible by 3, divisible by 5, or divisible by both 3 and 5. If it matches one of these conditions, we need to output the correct word.

So now you need to understand how an if statement is written into a program. In BASH, the basic syntax is this:

if [ <TEST> ]; then
        DO SOMETHING
fi

The preceding is a simple BASH if statement and it really isn’t that complex. You will notice that along with the if and the test and doing something, there is also a fi. The fi denotes the end of things that are to be performed, as you can perform multiple actions within an if statement. So, now we have our if statement, so we need to make it do things. So let’s have a quick look at some mathematical operations.

Image Note  Remember to leave a space on either side of the open square bracket, a space before the close square bracket, and a space after the semicolon; otherwise, you will get a syntax error.

Test Based Arithmetic

We know that we need to have a test for equality, so how does someone confirm if any given number is divisible by another given number? The simple answer is to check that when divided by a given number, the result is a “whole” number without any “remainder.” This is, in fact, as hard a concept to program as it sounds. Whereas multiplication, addition, subtraction, and even division will give finite answers that can be tested upon, checking whether something is divisible by another number requires a different operation. Thankfully, there is a specialized mathematical operation designed for just this issue: the modulus. The modulus returns the remainder of a division of any two numbers and is denoted by the symbol %. So the modulus gives you results like these:

12 % 3 = 0
12 % 5 = 2

And suddenly our test has appeared: if number modulus 3 is equal to 0 we output Fizz, if number modulus 5 is equal to 0, we output Buzz, and if number modulus 3 and 5 is equal to 0, we output FizzBuzz. So, if we combine all of these, we get the following:

if [ number % 3 = 0 ]; then
    echo "Fizz"
fi
if [ number % 5 = 0 ]; then
    echo "Buzz"
fi
if [ number % 3 = 0 ]; then
    if [ number % 5 = 0 ]; then
        echo "FizzBuzz"
   fi
fi

Okay, that’s a good-looking attempt, but there are a number of problems that relate to things about BASH we still need to cover in order to make this into valid executable BASH.

Let’s start with the simple variables. In BASH, there are two modes that variables operate in. First is assignment mode, which occurs when we are creating a new variable and giving it a value. In these instances, the variables work just like the previous number, so we could create our number variable as 1 this way:

number=1

This is fine and looks like our example, but when we want to use the current value of the number variable, we need to access the variable, which is done in dereferencing mode. In BASH, this means adding a dollar sign ($) to the front of every variable for which we want to use the value.

To recap, there are two modes for a variable: one for loading the variable (called assigning) and one for pulling the values out of the variable (called dereferencing). In the first mode, we use the variable as is; in the second, we use a $ in front of the variable. Okay, not a problem. Let’s update the code:

if [ $number % 3 = 0 ]; then
    echo "Fizz"
fi
if [ $number % 5 = 0 ]; then
    echo "Buzz"
fi
if [ $number % 3 = 0 ]; then
    if [ $number % 5 = 0 ]; then
        echo "FizzBuzz"
   fi
fi

So our code looks a bit better, but you may have noticed the next problem that occurred when we assigned a number the value of 1. The use of the equal sign (=) is used to assign values to variables, so in this context it looks like we are trying to assign the variable 5 with the number 0, which will cause all kinds of problems with the test in the if. Most languages handle this using a pair of equal signs (==) to denote a test for equality, but in BASH there are a number of special arithmetic testing operators we are given to test this:

  • -eq for equality
  • -ne for not equal
  • -gt for greater than
  • -lt for less that
  • -ge for greater than or equal to
  • -le for less than or equal to

We can take advantage of these operators in our code to do the comparison of the result of our modulus and 0. So make these changes to your code again and you should have the following:

if [ $number % 3 -eq 0 ]; then
    echo "Fizz"
fi
if [ $number % 5 -eq 0 ]; then
    echo "Buzz"
fi
if [ $number % 3 -eq 0 ]; then
    if [ $number % 5 -eq 0 ]; then
        echo "FizzBuzz"
   fi
fi

Finally, when doing an arithmetic operation (such as modulus), we need to tell BASH that this is meant as an arithmetic operation. To do this, we use a $ and a pair of parentheses around the actual bit of arithmetic so the operation of 5 % 3 means that we need $((5 % 4)), and we can make the change:

if [ $(($number % 3)) -eq 0 ]; then
    echo "Fizz"
fi
if [ $(($number % 5)) -eq 0 ]; then
    echo "Buzz"
fi
if [ $(($number % 3)) -eq 0 ]; then
    if [ $(($number % 5)) -eq 0 ]; then
        echo "FizzBuzz"
   fi
fi

There you have it; you have written three simple tests for the FizzBuzz application; and used if statements, a variable, the print, and the special arithmetic operator. That’s a lot of code, so let’s test it and make sure what we have got now works!

We can do this simple test by creating the number variable with a single value for now. Let’s assign it to 15 so we can guarantee some output. With the number variable added, your script should look like this:

#!/bin/bash
number=15
if [ $(($number % 3)) -eq 0 ]; then
    echo "Fizz"
fi
if [ $(($number % 5)) -eq 0 ]; then
    echo "Buzz"
fi
if [ $(($number % 3)) -eq 0 ]; then
    if [ $(($number % 5)) -eq 0 ]; then
        echo "FizzBuzz"
   fi
fi

When executed, the output will be this:

$ ./fizzbuzz.sh
Fizz
Buzz
FizzBuzz

Troubleshooting

Oh dear. That output is a problem. We said all three words at once, not just the FizzBuzz when the number was divisible by both 3 and 5. We just failed! Argh! Okay, take a few deep breaths; this isn’t the end of the world. In fact, it’s a great time to introduce a new feature. The if statement does something when the test is true, but there is also another part we can add: the else. The else is the flip side of the if, and the logic flows like this: if the test is true, do something, else do a different thing. We can use a combination of these two to perform our tests for FizzBuzz. The syntax of an if, else statement is as follows:

if [ TEST ]; then
    DO SOMETHING
else
    DO A DIFFERENT THING
fi

The syntax is almost identical to the original if statement, so it is easy to make a few changes to make your if statements into if, else statements. But now we run into the much bigger problem—and one that is the cause of most people’s problems with programming; it’s the logical order flow. We need to create a sequence of tests so that we can determine if something is divisible by 3, divisible by 5, or divisible by both; and perform a wholly different action in each case. This kind of thinking is what makes programming so hard for so many people, and there is no trick to it: you simply have to work it out. So let’s have a go at working this one out together.

If we find something that is divisible by 3 or 5, and output something before checking whether it is divisible by both, we run into the potential for outputting a Fizz, a Buzz, and a FizzBuzz, which is not what we want. So let’s start with a test for both:

 if [ $(($number % 3)) -eq 0 ]; then
    if [ $(($number % 5)) -eq 0 ]; then
        echo "FizzBuzz"
    fi
fi

Okay, now we have FizzBuzz, but what if the answer is divisible by 3 but not by 5 (which is the case for Fizz)? Then we can just output the Fizz. Thus, if we add an else to the divisible by 5 tests, we will be guaranteed a number that is divisible by 3 and not by 5. This means we avoid the issue when we output both Fizz and FizzBuzz at once. We can use the new else statement here to accomplish this, so when we add the else to the divisible by 5 test, we get this:

if [ $(($number % 3)) -eq 0 ]; then
    if [ $(($number % 5)) -eq 0 ]; then
        echo "FizzBuzz"
    else
           echo "Fizz"
    fi
fi

Fantastic. Now we only have one case left: when a number is not divisible by 3 but is divisible by 5. We can do this test in another else statement off the divisible by 3 test. By doing this as part of an else statement and adding a second divisible by 5 test, we can ensure that a number is not already divisible by 3 when we output when it is divisible by 5. This means we avoid running into the issue from before, so add this test in and your code becomes the following:

if [ $(($number % 3)) -eq 0 ]; then
    if [ $(($number % 5)) -eq 0 ]; then
        echo "FizzBuzz"
    else
           echo "Fizz"
    fi
else
    if [ $(($number % 5)) -eq 0 ]; then
        echo "Buzz"
    fi
fi

And that should do it! We have ourselves a new series of tests to run. Go ahead and replace the old statement in your script and run this new one:

$ ./fizzbuzz.sh
FizzBuzz

Perfect! That’s the correct result for 15! You can change the value of number and execute a few more times to test, but this test should stand up well because we have thought through our programming. Now we need to have the numbers from 1 to 100 counted. To do this, we will use the second logical operation!

Logical Operation: Loop

A loop is a special type of logical operation that functions at its core like an if statement, but instead of running the code if a statement is true, it will run the statement over and over again as long as the statement is true. This is how we will be able to increase the value of number by 1 over and over again to count to 100. A loop in BASH uses the following syntax:

while [ TEST ]; do
    DO SOMETHING
done

A loop is very similar to an if statement by design. Both are testing that some condition is true and both will then execute a section of code. The difference is that an if statement will execute only once and a while loop will execute until something is no longer true. This brings be to the first and biggest warning about loops: if you forget to have a leaving condition for your loop, then you won’t EVER leave it. This is called an infinite loop, in which your program will get stuck, do nothing, and run forever. There are a few situations when this is desirable but not many. Luckily for us we have the Ctrl+C shortcut that will send a terminate signal to whichever program we are running and we can use it to snap programs out of their infinite loops.

You might be thinking, “Oh, so that’s why so many programs get stuck!” This is 100% true. Sometimes cases that you may not have considered arise, and your program can wind up running in a loop forever, so consider yourself warned.

For our FizzBuzz program, we want to count the numbers 1 to 100, so we can do a simple test of numbers less than or equal to 100. So for all situations in which our number is less than or equal to 100, we will execute the loop. So let’s go ahead and write the loop around our existing block of tests. When done, it should look like the following. Don’t execute it yet because we have no way to increase the number from 1 to 100 (which means we’ll get an infinite loop because we can never reach 100 and leave the loop).

#!/bin/bash
number=1
while [ $number –le 100 ]; do
if [ $(($number % 3)) -eq 0 ]; then
    if [ $(($number % 5)) -eq 0 ]; then
        echo "FizzBuzz"
    else
           echo "Fizz"
    fi
else
    if [ $(($number % 5)) -eq 0 ]; then
        echo "Buzz"
    fi
fi
done

Okay, we have our code block, now we need to add the increasing number. We simply need to increase the value of number by 1 each time it goes around the loop. But we also want to perform all the calculations prior to changing the number. We can do this by using the arithmetic operations and assignment operation we have already. And we can add this block to the bottom of the code before the done statement:

number=$(($number +1))

Once you have made this change, execute your script! It will run and give you an output like this:

Fizz
Buzz
Fizz
Fizz
Buzz
Fizz
FizzBuzz
...

This is great, and it looks like it worked, but we should go ahead and print the current value of number for each trip round the loop. We could output the value at the start before we Fizz or Buzz or FizzBuzz, but that means we will have numbers on each line. Better if we can integrate it into our echo statements. To do this, we simply add the $number variable within the string of text to print. You learned earlier that the quotation marks mean everything is treated like a string of text, but there is one marker that supersedes it: the $ sign operator. This operator is used to make your life easier when you want to print out data from a variable in a block of text.

We should also add one final else statement, which will take care of the output of all the “other” numbers. Try and work it out for yourself; then check against this code:

#!/bin/bash
number=1
while [ $number -le 100 ]; do
if [ $(($number % 3)) -eq 0 ]; then
    if [ $(($number % 5)) -eq 0 ]; then
        echo "$number - FizzBuzz"
    else
           echo "$number - Fizz"
    fi
else
    if [ $(($number % 5)) -eq 0 ]; then
        echo "$number - Buzz"
    else
        echo $number
    fi
fi
number=$(($number +1))
done

And there you have it! A working FizzBuzz solution. Congratulations!

Troubleshooting

Having trouble with your code can be a bit of a nightmare. Probably the best tool at your disposal is the echo command. You can output anything you want, so if you are unsure of where your application is within your code, write an echo to output stuff and then check. Are you not entering the loop correctly? Output the variables that go into the test and compare by hand. Not sure why the if statement didn’t work? Add an else, output the full test case, and see if you went in correctly. Using these outputs to trace where you are in your code is the best way of doing diagnostics. Otherwise, pay attention to any errors; most should provide a line number and will tell you (in a manner of speaking) what’s wrong with what line. If you are in doubt, feel free to Google around because it’s almost certain someone, somewhere has had the same error before you and asked for a solution.

Practical BASH: An Init Script

You should be aware by now that most Linux applications are started by a special script called an init script. These scripts aren’t magic; they are simply scripts in BASH that take a given value and perform a series of actions based on what they are told to do—just as any good program does. The init scripts do have a small amount of special information in them, but none of this is actually program logic.

Let’s go through one together and examine exactly how one of these init scripts works. Following is the init script for XBMC, which you will see again later in this book:

#! /bin/bash
### BEGIN INIT INFO
# Provides:          xbmc
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start XBMC
# Description:       Start XBMC
### END INIT INFO
DAEMON=/usr/bin/xinit
DAEMON_OPTS="/usr/lib/xbmc/xbmc.bin"
NAME=xbmc
DESC=XBMC
RUN_AS=root
PID_FILE=/var/run/xbmc.pid
test -x $DAEMON || exit 0
set -e
case "$1" in
  start)
        echo "Starting $DESC"
        start-stop-daemon --start -c $RUN_AS --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
        ;;
  stop)
        echo "Stopping $DESC"
        start-stop-daemon --stop --pidfile $PID_FILE
        ;;
  restart|force-reload)
        echo "Restarting $DESC"
        start-stop-daemon --stop --pidfile $PID_FILE
        sleep 5
        start-stop-daemon --start -c $RUN_AS --background --pidfile $PID_FILE --make-pidfile --exec $DAEMON -- $DAEMON_OPTS
        ;;
  *)
        echo "Usage: /etc/init.d/$NAME{start|stop|restart|force-reload}" >&2
        exit 1
        ;;
esac
exit 0

The first thing you will notice is there are a lot of lines that start with #. These lines are comments; because # is the comment symbol in BASH, any line that starts with a # will not be executed as part of the application. The top lines of INIT INFO are in fact very important for an init script. These are special comments that can be processed to show how the application controlled by the script is to be run. The blocks look like this:

### BEGIN INIT INFO
# Provides:          xbmc
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start XBMC
# Description:       Start XBMC
### END INIT INFO

These blocks provide a few bits of description and say for which application they provide a function; in this case, the application provided is XBMC. They also mention what applications are needed to be running before this application can start or stop. The $all symbol means that this application will be started last, so that it is guaranteed that all other applications on which it can depend are started before it. Probably the more important set of operators are Default-Start and Default-Stop. These operators correlate with the Linux system’s run levels, which govern the various stages of the Linux system boot process.

The Linux specification defines the following run levels:

  • Level 0: Halt; shut down and power off
  • Level 1: Single User Mode; only basic system functionality (used for repairs)
  • Levels 2 % 3: Multi User; adds networking and multiple user functionality
  • Level 4: User Defined
  • Level 5: Normal System running state
  • Level 6: Reboot

The numbers next to the start and stop switches correlate to these levels, so XBMC will start in run levels 2–5 and will shut down in levels 0, 1, and 6.

Pick and Match with the case Statement

Once all the initialization is done and the script has set some initial variables, it performs a few quick tests and then moves on to the case statement. A case statement is very much like a series of running if statements against the same variable. Insofar as you take a given variable and for each of a series of given values for said variable, you perform a function. In most init scripts, this is used to confirm which action it is to perform.

The case statement in this start script is working on the special $1 variable. This variable represents the first thing passed to the script from the command line; so when trying to start the xbmc application, you will execute with this:

/etc/init.d/xbmc start

This gives the $1 variable the value of start. The case statement offers a number of different options of the potential value of the case statement:

  • start: Self-explanatory.
  • stop: Self-explanatory.
  • restart|force-reload: The values of restart or force-reload, which perform the stop application and then the start application in one process. The pipe (|) between them is used to denote an OR operation, which means that if either of the values here match, we treat this as having been a match.
  • *: Anything else.

After each case value, there is a small block of code that is executed in that case, followed by a pair of semicolons. These semicolons are like a fi and are used to denote the end of a block of case statement code.

Application within Application: Forking

Once within the case statement, you can see that each line will perform some action with the start-stop-daemon. But this isn’t a variable or anything special; it’s another program. The most powerful part of BASH is the bit we haven’t gotten to yet. BASH has the capability to execute any command-line process from within your programs just by using their names. Because BASH is the shell we use most of the rest of the time, this actually makes a lot of sense.

The last function of an init script: it will use the start-stop-daemon to create a new process for you. This action is called forking because you are forking something off of the currently running application. The start-stop-daemon is a small application that is used to fork off processes and will manage their running for you. The start script corrals the arguments for it, works out what action needs to be performed, and then tells start-stop-daemon which action needs to be taken against which application.

Now that you are armed with this knowledge, you should be able to write your own simple init scripts to start and stop any process you want by providing the starting comment block to describe how the script should be loaded, having a case statement determine which action to take, and finally executing the start-stop-daemon process to launch and manage your application!

Update the Run Files

Now you have a fully working script ready to go. You need to use chmod to set the executable flag on your file and then you can test it with the start and stop commands. It does as intended—fantastic! Now we need to add it into the system’s boot logic. Historically, this involved linking the file in a special way into the various run levels. However, this has been made much easier with the update-rc.d application. The update-rc.d application takes advantage of those special comment headers we added to our BASH scripts in order to know which run levels should have which shortcuts.

All we need to do to add the init script to the boot sequence is to issue the update-rc.d command with the name of the init script we want, which is xbmc in the example. Then we need to add the argument to say that we should use the defaults from the comments in the script; the argument unsurprisingly defaults. This gives us the command update-rc.d xbmc defaults, which will need to be executed as root. When run, the outputs should be as follows:

$ sudo update-rc.d xbmc defaults
update-rc.d: using dependency based boot sequencing

And that’s it; if you reboot, your application should run on boot!

Creating Your Own init Script

We have covered how init scripts are created; now we can create one. The process is relatively simple because we simply need to re-create the structure and input our own code in the correct place to perform the functions we need. You can have these init scripts do quite literally whatever you want, but for this example I will be creating a file with the touch command and then deleting it with rm when done. The touch command is used to simply create a file on disk with nothing in it; if it runs on an existing file, it will change the last modified time of the file. As mentioned earlier, you can have your programs managed by start-stop-daemon or you can simply perform system functions, as I will do in my example.

As you will recall, the first thing we need in this script is the shebang, which is the starting point for all BASH scripts and is thus the best place to start.

#! /bin/bash

Now that we have our shebang, recall the XBMC start script we worked with earlier: the first thing in that script is the opening comment block that gives us details about which init levels will run this script, and so on. So let’s add that next. You should fill in all your own details here. My script will be called touchfile.sh and will provide the Touch File service. My opening block of script will look like this:

### BEGIN INIT INFO
# Provides:          touchfile
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Run TouchFile
# Description:       Run TouchFile
### END INIT INFO

Now that I have defined the comment block, I can define my variables. They will be the variables that will cover what my application does. Anything you can think of needing in order to start this that can be configured should be defined as a variable. In the case of my touchfile command the thing I need to have defined as a variable is the filename of my touchfile. Remember that this is your code, and thus you can define literally anything you desire as a variable. My variable is this:

TOUCHFILE="/var/tmp/touch.file"

Now that my touchfile is defined, I need to add the case statement to cover the available actions. In this case, I want to have the following actions:

  • Start: Touch the file
  • Stop: Remove the touched file
  • Restart: Remove and then re-touch the file
  • Reload: Touch the file
  • Default Case: Tell people about how to use this file

This means I will have four case statements within my case. I will define the case statements (sans code to perform actions) as such:

case "$1" in
  start)
;;
  stop)
;;
  restart)
;;
  reload)
;;
  *)
;;
esac
exit 0

Now that we have the case entries, we just need to make them do something. I mentioned earlier what I want to do in each of the cases, so let’s write out the commands into each block and combine them into the script we have already. This will give us the following script:

#!/bin/bash
### BEGIN INIT INFO
# Provides:          touchfile
# Required-Start:    $all
# Required-Stop:     $all
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Run TouchFile
# Description:       Run TouchFile
### END INIT INFO
TOUCHFILE="/var/tmp/touch.file"
case "$1" in
  start)
        echo "Creating $TOUCHFILE"
        touch $TOUCHFILE
;;
  stop)
        echo "Removing $TOUCHFILE"
        rm $TOUCHFILE
;;
  restart)
        echo "Recreating $TOUCHFILE"
        rm $TOUCHFILE
        touch $TOUCHFILE
;;
  reload)
        echo "Re-Touching $TOUCHFILE"
        touch $TOUCHFILE
;;
  *)
        echo " Usage: touchfile.sh <start|stop|restart|reload>"
;;
esac
exit 0

And that’s it. The code is a simple init script, which accepts the start, stop, restart, and reload commands and outputs what it is doing and performs actions based on what arguments we provide it. This can be placed in /etc/init.d and can be set to be executable with chmod +x. Once they are done, you can test the script to ensure that it works. Finally, you can use update-rc.d to add this to your system boot process as we did earlier. It is in this fashion that you can write your own start scripts to run applications as you design.

Security and User Management

Security is one of the most neglected areas of systems administration. Because it is perceived as a black art to many, it often gets neglected.

The Rules of Raspbian Security

There are some very simple ways to make your system secure; just follow a few rules:

  • Don’t log in as root, but if you must, log out when done.
  • Use sudo whenever possible for admin tasks.
  • Choose a nontrivial password—one that is long enough and complex enough that it cannot be readily guessed or worked out by hand without taking a long time.
  • Change your password regularly.
  • Review your system logs regularly, specifically /var/log/auth.log because it lists all user authentications to your system.
  • If you don’t need an application running, don’t run it.
  • Expose as little of your system to the Internet as possible.
  • Restrict file permissions whenever possible.

Most of these rules will seem like common sense and that is because they are. Yet most people seem to fall into the trap of thinking that they won’t be attacked and ignore most of this advice. Now that you know these rules, you are probably saying, “I will change my password if only you would show me how!” Changing your password from the Linux command line is relatively easy: you simply use the passwd command and you will be prompted to enter a new password (twice, so that you can’t misspell it by accident the first time).

You can also change the password for other users as root. To do this, you simply append the name of the user whose name you are changing as the first argument of the command. This is incredibly useful when configuring new user details or resetting passwords.

Adding a New User

Because you aren’t supposed to use root at all times, it’s important to be able to create new users and understand how user creation takes place. In Linux operating systems, the users are governed by the file /etc/passwd. You can have a look for yourself and you will see a number of lines like this:

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh

These lines show the user, X for the password, the user’s ID number, the user’s group ID number, the user’s group identifier, the path of the user’s home directory, and the user’s shell. These are the basics of what is needed for a user to function within the Linux environment.

While this file has a password field, it is no longer used and normally contains a placeholder (in this case, X). The passwords are stored in the shadow file, /etc/shadow. The shadow file was separated so that only root could access password data and all the more common user data could be read by other more generic system applications.

Now that you understand how Linux stores users, add a new one called raspberry. To add this user, we will use the useradd command and a few arguments. There is another command, adduser, which performs user additions, but instead of being supplied with arguments will prompt you for details. adduser is a great command to use in a pinch when you forget a necessary argument for useradd.

First we want to specify the user’s home directory, which is done with the –d flag and the full path of the directory we want to use. Most home directories for users are /home/<username>, and we will do this for raspberry. Currently, /home/raspberry doesn’t exist, but before you rush out and add it, we can use the –m flag to tell useradd to do it for us! This gives us this command:

$ sudo useradd –d /home/raspberry –m raspberry

We could go a step further and even specify the group with –g <groupname> to set the primary group for the user. Or even specify the shell that the user would use with –s <shell path> or finally a password with –p <password>. Once you have executed your command, you can go and inspect the changes to /etc/passwd, where you should see something like this at the bottom of your file:

raspberry:x:1001:1001::/home/raspberry:/bin/sh

That’s it; you have now successfully added a new user! If you didn’t specify a password, you can use the passwd command we covered earlier to change your new user’s password and then you’re set to go. However, let’s say that you forgot to set the shell path with –s earlier and you want to change that so you start with /bin/bash rather than /bin/sh. Well, first you could edit the passwd file to change the shell or you can use the usermod command. The usermod command functions exactly the same as the useradd command, right down to the arguments; it simply adjusts the values. So run the following:

$ sudo usermod -s /bin/bash raspberry

We can expect the shadow file to be changed and, yes it is:

raspberry:x:1001:1001::/home/raspberry:/bin/bash

Fantastic! With these tools, you should be capable of creating new user accounts to allow people to access your Pi.

Summary

This chapter covered quite a number of things. We introduced SSH, DHCP and DNS. We covered some of the basics of how these systems function and how daemons are launched via init scripts. We then dove deep into learning BASH so that we could write our own init scripts. Finally, we covered some common security do’s and don’ts; then went on to cover how the basics of Linux systems manage users and their passwords. We even went so far as to create a whole new user ready for use.

These skills should enable you to manage the startup and networking of your Pi and should have given you an introduction into the wonderful world of software development!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.166.167