Přeložit do češtiny pomocí Google Translate ...

Labs: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14.

This lab is devoted to a mixture of maintenance tasks. It contains the bare minimum needed for management of your Linux machine. And it also contains few extras that might be useful for your shells scripts.

Users and root

Although we touched this topic already few times, it is probably better to state it explicitly at this point.

Among user accounts on a Linux system, one user has special privileges. The user is called root (or superuser), has numerical id of 0 and has virtually unlimited power over the running machine. For example, access rights are actually ignored for root user (i.e., process running under root ignores any of the rw privileges and can read/write any file).

Unlike on other systems, Linux is designed in such way that end-user programs are always executed under normal users and never require root privileges. As a matter of fact, some programs (historically, this was a very common behaviour for IRC chat programs) would not even start under root.

root is needed for actions that modify the whole system. This includes system upgrade, formatting of a hard-drive or modification of system-wide configuration files.

The strict separation of normal (work) accounts and a superuser comes from the fact that Linux was designed as a multi-user system. The philosophy dates back 50+ years where system was shared by many users and only one of them – root – was the administrator of the machine. Today, when typical notebook installations contain only one account, the separation is often more artificial but still exists.

The truth is that user today is threatened more by a malicious webpage rather than an unauthorized system software update and superuser account was designed to prevent the latter rather than the former. However, the idea of separate user accounts can be still valid today and a careful user may use different accounts for different activities (e.g. browsing social media vs working with your bank account).

sudo

Some programs need privilege escalation, i.e., run with higher privileges and wider permissions than other programs. Some need this by design and we already mentioned the sticky bit on executables that is used when the application always needs the elevated rights (and for any user actually launching the program). But the sticky bit is usable when we want to allow only certain group of users to run a particular command with elevated privileges. Furthermore, some commands require higher privileges only once in a while. Then there is no need to broaden the possible attack vectors.

For these situations, one option is sudo (homepage). As the name suggests, it executes (does) one command with superuser privileges. The advantage of sudo is that system admin can specify who can run which command with elevated permissions. Thus it does not give the allowed user unlimited power over the machine but only over a selected subset of commands. For example, it is possible to give a user option to restart a specific service (e.g. we want to allow a tester to restart a web server) without giving him control over the whole machine.

Note that the granularity of sudo stops at the level of programs. It does not restrict what sudo does inside the program. For example, it is possible to impose a restriction, that alice can execute dangerous_command only with --safe-option. However, if dangerous_command reads options also from a ~/.dangerousrc, alice can provide --unsafe-option there and sudo cannot prevent that. In other words, once the initial check is completed, the program runs as if it was launched under root.

That is extremely important for shared machines where the administrator typically wants to restrict all users as much as possible. On the other hand, for desktop installations, the typical default is that the first user created (usually during installation) can sudo anything. The reasoning is that it is the only (physical) user and knows the root password anyway. That is why most tutorials on web usually provide the commands for system maintenance including the sudo prefix.

However, you should always understand why you need to run sudo. Never get into the habit if it does not work, let’s try prepending sudo. Also note that there are multiple options for gaining a root shell (i.e., sudo bash).

Note that sudo is not the only security mechanism present. We will not discuss other mechanisms in great detail but just to give you pointer to documentation, there is also SELinux or AppArmor and high-level overview is on this Wikipedia page.

To try sudo, you can try running fdisk -l. With -l, it reads information about all disks on your system and displays information about individual partitions.

Without sudo, it will display probably only the following message.

fdisk: cannot open /dev/sda: Permission denied

Running it with sudo displays the actual information.

sudo fdisk -l
Disk /dev/sda: 480 GiB, 515396075520 bytes, 1006632960 sectors
Disk model: QEMU HARDDISK
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xdc505942

Device     Boot    Start        End   Sectors   Size Id Type
/dev/sda1  *        2048    2099199   2097152     1G 83 Linux
/dev/sda2        2099200   18620415  16521216   7.9G 82 Linux swap / Solaris
/dev/sda3       18620416 1006632959 988012544 471.1G 83 Linux

Note that sudo typically asks for a password (though that can be configured). It is password of the current user, not root’s (obviously, otherwise you could do su without needing sudo at all).

Package management

Software in Linux is usually installed through the means of package manager. The package manager is a special program that takes care of installation, upgrading and removing packages. A package can be anything that could be installed; this includes

  • a program (for example, package ranger installs the program ranger),
  • a data file or a configuration (e.g. libreoffice-langpack-cs for Czech support inside LibreOffice),
  • a library (e.g. gmp or gmp-devel providing the GNU arbitrary precision library),
  • or a meta package (e.g. xfce that covers xfce4-terminal, xfwm4-themes etc.).

In this sense, Linux is very similar to what you know from the shopping-center-style management of applications on your smartphones. It is very irregular to install software on Linux using a graphical install wizard.

The advantage of using centralized package manager is the ability to upgrade the whole system at once without the need to check updates of individual applications.

Individual packages often have dependencies – installing one package results in transitive installation of other packages the first one depends on (for example, a web browser will require basic graphical support etc.). It makes the upgrading process a bit more complicated (for the package manager, not for the user, though). But it can save some disk space. And the most important advantage is that different application share the same libraries (on Linux, they have .so extension and are somewhat similar to DLLs on Windows) and it is possible to upgrade a library even for an otherwise abandoned application. That is essential when patching security vulnerabilities.

Note that it is possible to install software manually too. From the file-system point of view, there is no difference – the package manager also just copies files to the right directories. However, manually installed software has to be upgraded manually too and generally complicates the setup. So avoid it when possible.

A typical package manager works with several software repositories. You can think about it as if your cell-phone has multiple marketplaces where to choose applications from. It is up to the user to decide which repositories he wants to use. Typically, you will encounter following types of repositories. It is up to each user (administrator) to decide which to use.

  • Stable and testing, where the latter provides newer versions of a software with slight possibility of bugs (usually, there is a third repository, often called unstable for the bleeding-edge).
  • Free and non-free where the former contains only software without any patent or royalty issues (usually based on US law).

It is also possible to setup your own repository that can be useful if you want to distribute your software to multiple machines (and you cannot publish the packages in the normal repositories because it is, for example, proprietary).

Most distributions also offer some kind of user-repositories support where virtually anyone can publish their software. For Fedora, this is done via Copr.

Note that both official and unofficial repositories offer no guarantees in the legal sense. However, using the official repositories of a given distribution is considered safe, the amount of attacks on software repositories is low and – unlike with many commercial organizations – distribution maintainers are very open in informing about the incidents. It is probably much easier to encounter a malicious application in your smartphone marketplace than to encounter it in an official distribution repository.

dnf (a.k.a. package manager in Fedora)

Note: Fedora used to have yum as the package manager and it can be found in many tutorials on the Internet (even in quite recent ones). It is considered obsoleted and you shall not use it. If you are used to yum from older versions of Fedora or from other RPM-based distributions, you will find dnf very similar and in many situations faster than yum.

The package manager for Fedora is called DNF.

You can use the search command to get a list of packages that matches the given name. Note that searching is not a privileged operation hence it does not require sudo.

dnf search arduino
dnf search atool

Note that searching for a very generic term can yield hundreds of results.

Note that the output is in the following format.

atool.noarch : A perl script for managing file archives of various types
ratools.x86_64

The .noarch and .x86_64 strings describe the nature of the package. noarch usually refers to a data package or package using interpreted languages, while .x86_64 denotes a package with binaries for x86-64 architecture (e.g., originally written in C or Rust).

To install a software, run dnf with install subcommand, giving it the package name to install. Here, sudo is needed as we are modifying the system.

sudo dnf install atool

Some applications are not part of any software repository but you can still download them in a format understandable by your package manager. That is a better situation than installing the files manually because your package manager knows about the files (although it cannot upgrade it automatically). One such example is the Zoom client that has to be installed like this.

sudo dnf install "https://zoom.us/client/latest/zoom_x86_64.rpm"

To upgrade the whole system, simply run sudo dnf upgrade. DNF will ask for confirmation and then upgrade all available packages.

Note that unlike on other systems, you can always choose when to upgrade. The system will never reboot the machine for you or display a message about needed restart unless you explicitly ask for it.

If you want to install the whole group of packages, you can use dnf grouplist to view their list and sudo dnf install @GROUP_NAME to install it.

The commands above contain the basics for maintaining your Fedora installation with respect to package management. Following links provide more information. The official Wiki page is a good source of information if you already know the system a bit.

For beginners, this guide about DNF and this tutorial are probably a better starting point.

Alternatives to classic package managers

The existence of various package managers has its disadvantages – when using multiple distributions, the user has to know how to operate different package managers. Furthermore, different distributions need to create different packages (compatible with their package managers), which results in more work.

Therefore, an effort has been made to unite the package managers. Snap was created in order to install packages among distributions uniformly. While for some users it is a way to get the software they want as quickly as possible, for some the proprietary nature of Snap and a need for an account at the package store presents potential dangers and shift in Linux open-source ideology.

To demonstrate a problematic example, let’s attempt to install PyCharm. PyCharm is an IDE for Python, which is (unfortunately) mostly directed at Windows users and also offers a charged professional version. No PyCharm package is offered in Fedora.

This is rather an exception – you won’t encounter problems with most open-source software. Actually, even companies that were traditionally oriented towards different OSes offer DNF-based repositories for their products these days. Note that in this case, providing a full repository is the ideal choice. Users can choose whether to enable this repository or not, distribution maintainers can focus on other tools and the company keeps full control over the release cycle and the distribution process.

There are these two options to install PyCharm:

  1. Use Snap
  2. Use the ad-hoc installation script. It is downloaded with the PyCharm installation.

Note that the second option is usually frowned-at in general. It requires running a shell script that the user downloads which is generally considered dangerous – you should always examine such scripts. (Obviously, using a package manager also involves downloading and running scripts but the attack surface is a bit smaller. The chances of following a wrong or malicious link are much lower compared to copying them manually from a chat, for example.)

Another issue is that any application downloaded in this way will not be automatically updated.

Which one to use

Snap is not the only alternative to the classic package managers.

Among others, there is Flatpak or AppImage. They can co-exist and it is up to the user to decide which one to choose.

The decision which one to use is influenced by many factors. Generally, using pre-packaged software distributed with your system (distribution) should be preferred.

As a last note – even if the software you want to install does not provide packages for your distribution, you can always create them yourself. The process is out-of-scope for this course but it is actually not very difficult.

File management

Because you already know ranger and mc and ls/cd and plenty of file-modification utilities, this section will be extremely short. It will only strengthen your knowledge about file archiving as other utilities were already covered in previous labs.

Archiving and compression

Archiving on Linux systems typically refers to merging multiple files into one (for easier transfer) and compression of this file (to save space). Sometimes, only the first step (i.e., merging) is considered archiving.

While these two actions are usually performed together, Linux keeps the distinction as it allows combination of the right tools and formats for each part of the job. Note that on other systems where ZIP-file is the preferred format, these actions are blended into one.

The most widely used program for archiving is tar. By default, it tries to access your tape archiving device (hence the name). Because you probably do not have one, you need to execute it with -f to specify tape-archive filename. Option -x is used when extracting files from the archive, option -c is used to create a new archive from existing files.

tar itself recognizes -z or -j (and few others) to automatically run a compression program on top of the archive. However, the general process for creating a compressed file is following.

tar -cf archive.tar dir_to_archive/
gzip archive.tar

tar creates archive.tar but gzip by default compresses archive.tar into archive.tar.gz and removes the original file (one could use -k of gzip to prevent that).

The same effect can be obtained by using the following shortcut.

tar -czf archive.tar.gz dir_to_archive/

Often gzip is used alone if you need to compress single file. Some tools or APIs can even except gzip-compressed file transparently.

Unpacking can be again done in two steps or in a single one with the following command.

tar -xzf archive.tar.gz

Note that like many other file-system related programs, tar will overwrite existing files without any warning.

We recommend to install atool as a generic wrapper around tar, gzip, unzip and plenty of other utilities to simplify working with archives.

apack archive.tar.gz dir_to_archive/
aunpack archive.tar.gz

Note that atool will not overwrite existing files by default (which is another very good reason why to use it).

Note that it is a good practice to always archive a single directory. That way, user that unpacks your archive will not have your files scattered in the current directory but neatly prepared in a single new directory.

To view list of files inside an archive, you can execute als or tar -tf archive.tar.gz (execution with -tvf instead of -tf prints also permission bits and other details).

Storage management

Files resides on file systems that are the structures on the actual block devices (typically, disks). Working with file systems and block devices is necessary when installing new system, rescuing from a broken device or simply checking available free space.

Mounts and mount-points

Recall that each file system (that we want to access) is accessible as a directory somewhere (compared to a letter drive in other systems, for example). When we can access /dev/sda3 under /home we say that /dev/sda3 is mounted under /home, /home is then called the mount point, /dev/sda3 is often called a volume.

Most devices are mounted automatically during boot. This includes / (root) where the system is as well as /home where your data resides. Note that /dev, /proc (among others) are special file systems that are mounted to these locations. Hence, the file /proc/uptime does not physically exist (i.e., there is no disk block with its content anywhere on your hard-drive) at all.

The file systems that are mounted during boot are listed in /etc/fstab. You will rarely need to change this file on your laptop and this file was created for you during installation. Note that it contains volume identification (such as path to the partition), the mount point and some extra options.

When you plug-in a removable USB drive, your desktop environment will typically mount it automatically. Mounting it manually is also possible using the mount utility. However, mount has to be run under root to work (this thread explains several aspects why mounting a volume could be a security risk). Therefore, you need to play with this on your installations where you can become root. It will not work on any of the shared machines.

Technical note: the above text may seem contradictory, as mount requires root password yet your desktop environment (DE) may mount the drive automatically without asking for any password. Internally, your DE does not call mount but uses a different program (today, often based on Udisks and Polkit) that together verify that the mounted device is actually a removable one and that the user is a local one (i.e., it will not work over SSH). This program has sticky bit set and thus runs with root privileges automatically (the concrete implementation may slightly differ).

To test the manual mounting, plug-in your USB device and unmount it in your GUI if it was mounted automatically (note that the usual path the device is mounted is somewhere under /media).

If you have rotational disk, your USB will probably be available as /dev/sdb1, if you have SSD, your USB drive will become /dev/sda1 (consult the following section about lsblk to view list of drives).

sudo mkdir /mnt/flash
sudo mount /dev/sdb1 /mnt/flash

Your data shall be visible under /mnt/flash.

To unmount, run the following command.

sudo umount /mnt/flash

Note that running mount without any arguments (and even without sudo – recall text above) prints list of currently active mounts.

Working with disk images

Linux has a built-in support for working with disk images. That is, with files with content mirroring a real disk drive. As a matter of fact, you probably already worked with them when you setup Linux in a virtual machine or when you downloaded the USB disk image at the beginning of the semester.

Linux allows you to mount such image as if it was a real physical drive and modify the files on it. That is essential for the following areas.

  • Developing and debugging file systems (rare)
  • Extracting files from virtual machine hard-drives
  • Recovering data from damaged drives (rare, but priceless)

Note that for recovering data from damaged drives, the typical approach is to try to copy the data from the file as-is on the lowest level possible (typically, copying the raw bytes without interpreting them as a file system or actual file). Only after you recover the disk (mirror) image, you run the actual recovery tools on the image. That prevents further damage to the hard-drive and gives you plenty of time for the actual recovery.

In all cases, to mount the disk image we need to tell the system to access the file in the same way as it accesses other block devices (recall /dev/sda1 from the example above).

There are, as usual, several options. We can run mount with -o loop to do it in one step or run udisksctl loop-setup to add the disk image as a removable drive that could be automatically mounted in your desktop.

Recall that mount requires sudo privileges hence you need to execute the following example on your own machine, not on any of the shared ones.

To try that, you can download this FAT image and mount it.

# Using mount
sudo mkdir /mnt/photos-fat
sudo mount -o loop photos.fat.img /mnt/photos-fat
... (work with files in /mnt/photos-fat)
sudo umount /mnt/photos-fat
# Using udisksctl and auto-mounting in GUI
udisksctl loop-setup -f fat.img
# This will probably print /dev/loop0 but it can have a different number
# Now mount it in GUI (might happen completely automatically)
... (work with files in /run/media/$(whoami)/07C5-2DF8/)
udisksctl loop-delete -b /dev/loop0

Disk space usage utilities

The basic utility for checking available disk space is df.

Filesystem     1K-blocks    Used Available Use% Mounted on
devtmpfs         8174828       0   8174828   0% /dev
tmpfs            8193016       0   8193016   0% /dev/shm
tmpfs            3277208    1060   3276148   1% /run
/dev/sda3      494006272 7202800 484986880   2% /
tmpfs            8193020       4   8193016   1% /tmp
/dev/sda1        1038336  243188    795148  24% /boot

In the default execution (above), it uses bytes. For a more readable output, run it with -BM or -BG (megas and gigas) or with -h to let it select the most suitable unit.

Do not confuse df with du that can be used to estimate file space usage. Typically, you would run du as du -sh DIR to print total space occupied by all files in DIR. You could use du -sh ~/* to print summaries for top-level directories in your $HOME. But be careful as it can take quite some time to sum everything and may render the machine less responsive (it depends on many factors but do not be surprised).

To see how volumes (partitions) are nested and which block devices your kernel knows about, you may use lsblk. On the shared machine, following will appear.

NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
sda      8:0    0   480G  0 disk
├─sda1   8:1    0     1G  0 part /boot
├─sda2   8:2    0   7.9G  0 part [SWAP]
└─sda3   8:3    0 471.1G  0 part /

This shows that the machine has 480G disk divided into three partitions. Tiny /boot for boostrapping the system, swap partition of 8G and about 470G are left for system and user data.

Using lsblk -o FMT allows you to customize the output format.

Inspecting and modifying volumes (partitions)

We will leave this topic to a more advanced course. If you wish to learn by yourself, you can start with the following utilities.

  • fdisk(8)
  • btrfs(8)
  • lvs(8), pvs(8) and related ones

Process management and signals

When you start a program (i.e., an executable file), it becomes a process. The executable file and a running process share the code - it is the same in both. However, process also contains the stack (e.g., for local variables), heap, list of opened files etc. etc. – all this is usually considered a context of the process. Often, words running program and process are used interchangeably.

To view list of running processes on your machine, you can execute ps -e. However, for interactive inspection, htop is a much nicer alternative.

We can use htop to view basic properties that a Linux system binds with each process.

First of all, each process has a process ID, often just PID (but not this one). PID is a number assigned by kernel and used by many utilities for process management. PID 1 is used by the first process in the system (0 is reserved as a special value – see fork(2) if you are interested in details), when this process terminates, the system shuts down. Other processes are assigned PIDs incrementally (more or less) and PIDs are eventually reused.

Note that all this information mentioned below is also available under /proc/PID/.

Each process has its user that executes it. It is used to determine access to files, so practically to every resource (recall that everything is eventually represented as a file). Note that this information is also visible from the ownership of /proc/PID/.

htop then shows memory occupied by the process and CPU utilization. Interpreting these values it not always straightforward as many applications seem to occupy hundreds of megabytes of virtual memory but practically need only few kilobytes of physical memory to work. But using these columns to check that some program is not using disproportionally too much memory or CPU is certainly fine. Machine-readable interface is available under /proc/PID/status.

The TIME column shows how long a process is running. Available under /proc/PID/stat as well (though the format is a bit daunting at first).

Finally, the last column shows the command-line that was used to execute the program. Mirror of /proc/PID/cmdline.

Signals

Linux systems use the concept of signals to communicate asynchronously with a running program (process). The word asynchronously means that the signal can be sent (and delivered) to the process regardless of its state. Compared to communication via standard input (for example), where the program controls when it will read from it.

However, signals do not provide a very rich communication channel. As a matter of fact, kernel defines which signals (i.e., signal numbers) exist and handles some of them by itself (which usually means that the process in question is forcefully terminated). Other signals can be intercepted by the application and acted upon. If the signal is not intercepted by the application, the kernel terminates the application by default.

This is actually expressed in the fact that the utility used to send signals is called kill (because usually the target process terminates).

By default, the kill utility sends signal 15 (also called TERM) that instructs the application to terminate. An application may decide to catch this signal, close its opened files etc. and then terminate. Apart from TERM, we can instruct kill to send KILL signal (number 9) that is handled by kernel only and immediately and forcefully terminates the application (signal 9 is non-maskable as it cannot be intercepted from within the application).

Most of the signals are sent to the process in reaction to a specific event. For example, signal PIPE is sent when a process is part of a pipe and some of the other processes in the pipe terminate prematurely (i.e., when they close their input and the application has nowhere to write their data). Typically, applications do not react to majority of the signals and kernel terminates them. (Remember the issue from lab 04.)

For example, when the system is shutting down, it sends TERM to all of its processes. This gives them the option to terminate cleanly. Processes that are still alive after some time are killed forcefully with KILL.

Your program would typically react to TERM (the default “soft” termination), INT (Ctrl-C from the keyboard) and perhaps to USR1 or USR2 (the only user-defined signals). Note that traditionally, reaction to USR1 is reloading program configuration.

Following Python program reacts to Ctrl-C by terminating (imports omitted).

# Actual signal callback
def on_signal(signal_number, frame_info):
    print("")
    print("Caught signal {} ({})".format(signal_number, frame_info))
    sys.exit()

def main():
    # Setting signal callback
    signal.signal(signal.SIGINT, on_signal)
    while True:
        time.sleep(0.5)
        print("Hit Ctrl-C...")

if __name__ == '__main__':
    main()

Exercise: write a program that tries to find highest prime number. When terminating, it stores the highest number found so far and on next invocation it continues from there. Solution.

Reacting to a signal in a shell script is also possible using the trap command. Note that a typical action for a signal handler in shell is clean-up of temporary files.

#!/bin/bash

set -ueo pipefail

on_exit() {
    echo "Cleaning up..."
    rm -f "$MY_TEMP"
    trap - EXIT
    exit 0
}

MY_TEMP="$( mktemp )"

trap "on_exit" INT QUIT TERM EXIT

echo "Running as $$"

counter=1
while [ "$counter" -lt 30 ]; do
    date "+%Y-%m-%d %H:%M:%S | Waiting for Ctrl-C (loop $counter) ..."
    echo "$counter" >"$MY_TEMP"
    sleep 1
    counter=$(( counter + 1 ))
done

The command trap receives as the first argument the command to execute on the signal. Following arguments are signals to react to. Note that a special signal EXIT means normal script termination. Hence, we do not need to call on_exit after the loop terminates.

Using - instead of the handler causes the respective handler to be set to default. Without this reset in on_exit, the handler would be called twice after the user would hit Ctrl-C (first for INT caused by Ctrl-C itself and second by the explicit call to exit).

From now on, your shell scripts shall always include a signal handler for clean-up of temporary files.

Note the use of $$ which prints current PID. Alternatively, you can use pgrep <program_name> to find a PIDs for running programs. Similarly, you can use killall to kill processes by name (but be careful as with great power comes great responsibility). Consult these with manual pages for more details.

Run the above script, note its PID and run the following in a new terminal.

kill THE_PID_PRINTED_BY_THE_ABOVE_SCRIPT

The script was terminated and the clean-up routine was called. Compare with situation when you comment-out the trap command.

Run the script again but pass -9 to kill to specify that you want to send signal nine (i.e., KILL).

What happened? Answer.

While signals do not offer a rich communication (richer communication channel is offered, for example, by D-Bus) they are at the core of rudimentary process control. Reasonable reaction to basic signals is a must for server-style applications (e.g., a web server upon receiving TERM should complete outstanding requests but do not accept new ones and terminate afterwards) and a good style for larger shell scripts (at least, to clean-up temporary files).

Graded tasks

Note that all tasks are supposed to be submitted to GitLab as usual, some of them require to be prepared on the remote machine (but we will check only files in your submission repository).

08/lorem.tar.gz (15 points)

Archive the directory linux.ms.mff.cuni.cz:/srv/nswi177/lorem into 08/lorem.tar.gz.

Ensure that unpacking your archive will result in creation of the lorem directory (i.e., only the last one, not full /srv/nswi177/lorem).

It is necessary to prepare the compressed archive at the remote machine in order to get points for this task (i.e., do not copy the files from /srv/nswi177/lorem to your machine first). After the archiving and compression is done, upload the file to GitLab as usual.

08/fat.txt (15 points)

File linux.ms.mff.cuni.cz:~/lab08.fat.img is a disk image with a single file. Paste its (decompressed) content into 08/fat.txt (to GitLab).

Note that we can create the source file ~/lab08.fat.img only after you login to the remote machine for the first time.

If the file is not there, wait for the next work day for the file to appear.

Do not leave this task for the last minute and contact us if the file has not appeared as explained in the previous paragraph.

08/proc_read_stats.sh (15 points)

One of the files under /proc/[pid] contains (approximate) information about number of bytes read and written by a process (i.e., the rchar and wchar statistics). Write a shell script that produces output in the following format for all processes on the machine. Lines starting with # are comments and does not have to be in the real output.

# PID : read bytes : written bytes : command-line (cmdline)
# If the information is not available, dash is inserted
1:-:-:/sbin/init
171:40619189:12923788:bash
56824:-:-:-

You do not have to deal with a situation when a process disappears in the middle of the query but your output has to be well formatted at all times.

To allow testing, your script must support reading alternate /proc location from the variable PROCFS_BASE. In other words, look into ${PROCFS_BASE:-/proc}.

Sort the output by PID (i.e., run the final output through sort -n).

Do not print any errors when the process suddenly disappears or when some files are missing (i.e. due to the fact that your list of process from the time you start the script can be different from the list at the end of your script execution).

Update: although it would make sense (because of the focus of the whole lab) to use pgrep or ps, please, scan the file-system. Otherwise, we will not be able to test your solution (see also this discussion).

08/quiz.md (40 points)

Fill-in the quiz from linux.ms.mff.cuni.cz:/srv/nswi177/lab08-quiz.md and upload it to GitLab (as usual).

Ensure that you place your answer in the **[A1]**-style markers so that we can find it easily.

The point of the quiz is not to check that you can copy a text from the Internet but to ensure you understand the principles. Explain things in your own words, feel free to use bullet lists.

Please, try to make your answers concise where possible (obviously, correctness is preferred over brevity but text several pages long is not something we expect for these answers). Thank you!

Update: we managed to upload an older version of the file that stated a total sum of 45 points. Fix was already uploaded, the correct sum is 40 points (the last set of questions is for lower amount of points because they are really easy). Sorry.

08/countdown.sh (15 points)

Write a script which gets one parameter: number of seconds (nonnegative integer) to count down, and each second it prints time left.

If user hits Ctrl-C during execution (or sends the TERM signal), the script aborts by printing the word Aborted and exits with the status of 17.

Example:

$ ./countdown.sh 10
10
9
8
7
6
5
4
3
2
1

Each line will appear a second after the previous one. Use sleep 1 to wait between printing the lines and before exiting. Therefore, the first number is printed immediately, whereas after the final line with 1 you still have to wait before your script terminates.

Note that the script will be a little bit slower than the requested time in seconds (because of the overhead of calling the sleep command itself) but that is fine.

Example execution when user hits Ctrl-C is as follows (^C denotes the place where the user hit Ctrl-C and is not an output from your program). Note that the FAILED message comes from the echo and serves only to emphasize the non-zero exit code.

$ ./countdown.sh 5 || echo "FAILED"
5
4
^C
Aborted
FAILED

Deadline: May 10, AoE

Solutions submitted after the deadline will not be accepted.

Note that at the time of the deadline we will download the contents of your project and start the evaluation. Anything uploaded/modified later on will not be taken into account!

Note that we will be looking only at your master branch (unless explicitly specified otherwise), do not forget to merge from other branches if you are using them.

Changelog

2021-05-10: Add information about als and tar -tf.