ROS-Ready

6. Advanced Linux

Advanced Linux topics for ROS development

12 min read 1 task with points

Summary

Estimated time to completion: 1.0 hours

What will you learn with this unit?

  • Permissions
  • Advanced Bash Scripts (with ROS)
  • The .bashrc File
  • Environment Variables

Permissions

Although there are already a lot of good security features built into Linux-based systems, one very important potential vulnerability can come from file permission-based issues resulting from a user not assigning the correct permissions to files and directories.

Let's start by executing a command we already know.

bash
ls -la src/first_script.sh
output
-rw-r--r-- 1 username username 255 Jul 16 13:43 src/first_script.sh

Here we use ls -la, the -la flag allows us to see some basic data related to our script, like the permissions of the files/folders, their size, etc.

The output is structured as follows:

output
<permissions> <owner> <group> <size> <Creation date> <file/folder name>

Each file or directory has 3 permissions types:

  • read: The Read (r) permission refers to a user's ability to read the contents of the file. It is stated with the character r.
  • write: The Write (w) permission refers to a user's ability to write or modify a file or directory. It is stated with the character w.
  • execute: The Execute (x) permission affects a user's ability to execute a file or view the contents of a directory. It is stated with the character x.

Owner and Group

Each file or directory has three user-based permission groups:

  • owner: Current permissions the file has for others not in the group. The Owner permissions apply only to the owner of the file or directory and will not impact the actions of other users. They are represented in the first 3 characters (rw-) of the ls output.
  • group: Current permissions the file has for users in the same group. The Group permissions apply only to the group that has been assigned to the file or directory, and will not affect the actions of other users. They are represented in the middle 3 characters (r--).
  • all users: The All Users permissions apply to all other users on the system, and this is the permission group that you want to watch the most. They are represented in the last 3 characters (r--).

So, applying all this to our file, we can say that the owner of the file (in this case, it's us) has read (r), write (w), and no execute (x) permissions, and the group and the rest of users have only read (r) permissions.

Below you find the complete secret to master this spell.

bash
rw-r--r--

So, as you may have already deduced, the only permissions that apply are the ones that are explicitly specified with their character. If they appear with a - symbol, it means that the permissions are not applied to the file or folder.

From this, we can see that this file has no execution permissions. And this, if we want to actually execute the file, could be a problem, don't you think? Then... how can we change this?

The chmod Command

In Unix-like operating systems, the chmod command is used to change the access mode of a file.
The name is an abbreviation of "change mode."
It is used to modify the permissions of a given file or directory (or many of them). However, there are a few ways to utilize this command, so let's have a look at them one by one.

Change the directory to the folder containing our first_script.sh.

bash
cd src

Let's use the chmod command to provide the file execution permissions.

bash
chmod +x first_script.sh

Let's check if the permissions have changed!

bash
ls -la first_script.sh
output
-rwxr-xr-x 1 username username 255 Jul 16 13:43 src/first_script.sh

The execution permissions, symbolized by the character x, have been added to all permission groups, as you can see.

Now, how do we only modify, let's say, the user's permissions?

chmod in-depth

The syntax of chmod is as follows:

bash
chmod  [groups to assign the permissions][permissions to assign/remove] [file/folder names]

The remaining two parameters are already known (permissions to assign/remove, file/folder names). The groups can be specified by using the following flags:

  • u: Owner
  • g: Group
  • o: Others
  • a: All users (does not need to be specified)

To better understand this, let's write down some examples. What would the command be if we simply wanted to give write permissions to the group? Can you make a guess? The solution is right here:

bash
chmod g+w first_script.sh

The permissions would then be changed as a result of the command above to the following:

bash
ls -la first_script.sh
output
-rwxrwxr-x 1 username username 255 Jul 16 13:43 src/first_script.sh

What if we now wish to delete the execution rights for the group and others, for example? This is what it would look like:

bash
chmod go-x first_script.sh

The permissions would then be changed as a result of the command above to the following:

bash
ls -la first_script.sh
output
-rwxrw-r-- 1 username username 255 Jul 16 13:43 src/first_script.sh

Is everything clearer now? If you want to get used to it, you can keep doing tests. However, as I said at the start of this section, there is another way to change a file's permissions: Binary References.

Binary References (Octal Values)

It's actually fairly straightforward. Basically, three integers are substituted for the entire string specifying the permissions (rwxrwxrwx). The first number indicates the Owner permission, the second number reflects the Group rights, and the third number represents all other user permissions.

How do these figures work?

In general, each permission is assigned a number:

  • r = 4
  • w = 2
  • x = 1

Then you sum the numbers together to get the integer/number that corresponds to the permissions you want to set. For each of the three permission groups, you'll need to include the binary permissions.

For example, let's imagine we wish to grant the owner complete control (rwx). Then we'd add 4 + 2 + 1 to get 7.

We only want to offer read permissions to the Group now (r--).
Then it'd just be 4.

Finally, we don't want to grant any permissions to any other people.
That would be a 0 then.

As a result, we'd wind up with 740. The final command would be as follows:

bash
chmod 740 first_script.sh

The permissions would then be changed as a result of the command above to the following:

bash
ls -la first_script.sh
output
-rwxr----- 1 username username 255 Jul 16 13:43 src/first_script.sh

See the following figure for an overview of the octal code meanings.

Octal ValueFile Permission SetPermission Description
0---No permissions
1--xExecute only
2-w-Write only
3-wxWrite and execute
4r--Read only
5r-xRead and execute
6rw-Read and write
7rwxRead, write, and execute

Summary

We have learned that:

  • There are 3 permissions: read (r), write (w), execute (x).
  • There are 4 main groups for the filesystem in Linux: owner (o), group (g), others (o), all (a).
  • We can change them in two ways:
    • By directly specifying permissions at a time, e.g., chmod +rwx.
    • By changing multiple with octal codes, e.g., chmod 700.

For a detailed explanation of the above, see: link

Advanced Bash Scripts

What you will learn:

  • Passing arguments to bash scripts
  • Running commands in the background
  • Removing noisy output from our terminal
  • While and For loops
  • If Queries
  • Killing processes

We will do that by creating a bash script that:

  • Launches a launch file of the basic_linux_pkg ROS 2 package (no roscore needed — DDS handles discovery automatically)
  • Handles two if queries
  • Kills processes based on their PID number
bash
#!/bin/bash

ARG1="$1" # arguments passed to a script range from $1 ... $N, the number of args is stored in the variable "$#"
echo "Arg= $ARG1"
echo "Argument count= $#"

# ROS 2 has NO roscore / rosmaster — every node discovers its peers over DDS.
# We just launch our own stuff in the background.
ros2 launch basic_linux_pkg unit1.launch.py &>/dev/null &   # The & runs the launch in the background so script flow can continue.
                                                             # &>/dev/null discards stdout + stderr (the null device is a file that
                                                             # reports every write as successful while throwing the bytes away).

sleep 2    # Give the launch a moment to spin up the nodes before we start issuing commands to them.

if [ "$ARG1" == 'backward' ]; then  # If queries as well as while loops require the spaces next to the [ and ]
                                    # Here we compare two strings
    echo "Executing: ros2 run basic_linux_pkg move_robotino backward"
    ros2 run basic_linux_pkg move_robotino backward    # We use the ROS 2 CLI
fi                   # the fi querie is closed by the fi command

if [ 1 -ne 0 ]; then # Bash also supports numeric comparison (built in only for integers)
    # Here we check if 1 is Not Equal to 0
    echo "1 not equal 0"
fi

if [ 5 -gt 2 ]; then # Here we check if 5 is greater then 0
    echo "5 is greater 2"
fi


# Read -p waits untill enter is pressed
read -p "Press enter to stop the bash script by:
    - killing the simulation (gazebo)
    - killing all ROS 2 nodes
    - performing cleanup"

killall -9 gzclient || true    # killall: searches for the given process name and kills it
killall -9 gzserver || true    # -9: Is the SIGKILL signal, causes the process to terminate immediately (kill) aka. avada kedavra

# No `rosnode kill` in ROS 2 — the equivalent is to kill the process itself.
for proc in $(ros2 node list); do
    echo "Killing node $proc"
    pkill -9 -f "$(basename "$proc")" || true
done

return 0

Summary

We have learned that:

Exercise 10 min+10 pts

Exercise 1 [10 min]

Create a bash script exercise1.sh that does the following:

  1. Launch the unit1.launch.py file from the basic_linux_pkg ROS 2 package (no roscore needed — ROS 2 uses DDS for discovery)
  2. It will receive one parameter, which will contain either:
  • circle
    • If the parameter is circle, the script will call move_bb8_circle from the move_bb8_pkg package.
  • forward_backward
    • If the parameter is forward_backward, the script will execute move_bb8_forward_backward from the basic_linux_pkg package.
  • square
    • If the parameter is square, the script will call move_bb8_square from the move_bb8_pkg package.
  1. Stop all Gazebo-related stuff
  2. Stop all ROS 2 nodes

Solution

The .bashrc File

You just learned about bash scripts in the previous part. Let me now explain you to a unique bash script. I'm referring to the.bashrc file. The.bashrc file is a special bash script that Linux runs every time a new Shell session is started.

When a user opens a new terminal, the.bashrc file is executed as a script. The file itself contains a number of terminal session configurations:

  • Coloring
  • Code completion
  • Shell history
  • Command aliases
  • Other features (eg. defining custom functions/ aliases)

The.bashrc file is a hidden file (since it starts with a .), as you may have noticed. It is created by the Linux system automatically for every user and is always saved in the HOME folder of the user (/home/user/.bashrc). However, you may still change it to make your Shell experience more personal. The .bashrc file contains a lot of comments that make it easy to understand, let's have a peek at the contents of our.bashrc file.

bash
cat $HOME/.bashrc   # The HOME environment variable contains the absolute path to the currents user home directory (/home/user)

The output will be a long list of comments and configurations. You can also open the file with a text editor to see the contents.

Now, as previously stated, the.bashrc script is run every time a new Shell session is started. However, if you want to force the execution from within a Shell, there is a command that will cause the.bashrc file to be executed. The source command is what we are looking for.

Let's modify our .bashrc:

Open the .bashrc file with your favorite IDE:

bash
vim $HOME/.bashrc

Press G to move to the last line of the file. Press g_ to move to the last non-blank character of the line. Press o to open a new line.

Paste the following commands at the end of the file

bash
echo "Bashrc finished initializing"
export MY_WAND="$(which bash)"
MY_SPELL()
{
    echo "My spell executed"
}

Press ESC, then :wq for write and quit and ENTER to exit the insert mode.

Open a new terminal and run the following commands:

bash
source ~/.bashrc
output
Bashrc finished initializing
bash
echo $MY_WAND
output
/usr/bin/bash
bash
MY_SPELL
output
My spell executed

The here used source command is a shell built-in command which is used to read and execute the content of a file.

Summary

We have learned that:

  • Bashrc is a shell script which is run every time a user opens a new shell
    • Configures environment variables/ settings
  • For more about the .bashrc and how to configure it click here

Environment Variables

An environment variable is a dynamically designated value that can influence how computer processes behave.

They are a component of the process's environment. A running process, for example, can look up the directory structure owned by the user running the process by querying the HOME variable.

For example, the PS1 variable, which sets the color of the Shell prompt. Furthermore, in Robotics Development and ROS, environment variables are widely used.

export Command

When you start a new Shell session, the environment variables are set via the .bashrc file. The Shell has no means of knowing if you modify any of the variable values throughout the different Shell sessions. The export command allows you to notify the current Shell and session of a modification you made to an exported variable.

You don't have to wait until the next Shell session to use the variable's new value. In fact, we did exactly that in the previous section: we used the export command to set the variable MY_WAND and created a custom dunction MY_SPELL in the .bashrc. When we used the source command to run the .bashrc script, it also ran the export command, as well as a custom function creation.

O.W.L. exam0 / 50 pts

Ordinary Wizarding Level

7 questions · 5 pts first try · 2 pts on a later try · +15 bonus for a clean sweep

Question 1 of 7

In -rwxr-xr-x, which part belongs to the group?

On this page