Linux system programming in C
You work with files, processes, signals, pipes and shared memory directly through system calls and write services that shut down cleanly and drop privileges.
Level 301 · 23 lessons · 24 practice labs
Lessons
System calls and files
- What a system call is: between libc and the kernel
- Low-level I/O: open, read, write, close
- File descriptors and redirection with dup2
- stat, permissions and umask
- File locking: flock and fcntl
- Short task: cat using only system calls
Processes
- fork: splitting a process in two
- The exec family and environment variables
- wait, waitpid and zombie processes
- Pipes: passing data between processes
- Short task: a program that pipes two commands together
Signals and time
- Signals and sigaction
- Safe functions in signal handlers
- Timers: alarm, nanosleep and timerfd
- Writing a background service and running it under systemd
- Short task: a service that stops cleanly on SIGTERM
Memory and communication
- Mapping a file into memory with mmap
- Shared memory and POSIX semaphores
- UNIX domain sockets
- User IDs, setuid and dropping privileges
- Watching file changes with inotify
- Short task: a watcher that logs changed files
Capstone
- Capstone: a supervisor that restarts a crashed job
Practice labs
- Completing partial write calls: Write a write_all function that loops over partial writes and also handles EINTR.
- Reading the last bytes of a file with lseek: Write a program that prints the last N bytes of a file using only open, lseek and read.
- Redirecting stderr to a log file: Redirect a program's stderr to a log file with dup2 and verify through /proc that no descriptors leak.
- Running only one copy at a time: Write lock logic with flock so that if two copies of a backup job start together the second one is refused.
- Reporting world-writable files: Read file permissions in a folder with stat and list the files anyone can write to.
- Creating a secret file with the right mode: Fix code that creates a key file so it uses mode 0600 and O_EXCL regardless of the umask.
- Record-level locking with fcntl: Lock only the relevant record of a shared counter file with fcntl so two processes update it without losing writes.
- Collecting child exit codes: Write a program that starts three children and reports each one's exit code with waitpid and WEXITSTATUS.
- Running a program with a clean environment: Run a helper with execve using only allowed environment variables and a fixed PATH.
- Cleaning up zombie processes: Add a SIGCHLD handler and a waitpid loop to a job launcher that piles up zombies so none show in ps.
- Building a three-stage pipeline: Write a program that chains three commands with two pipes, closing unused ends so it never hangs.
- Reloading settings on SIGHUP: Complete a service that sets a flag on SIGHUP with sigaction and rereads its settings in the main loop.
- Making a signal handler safe: Rewrite a signal handler that calls printf and malloc to use only safe functions and volatile sig_atomic_t.
- Sending a heartbeat with timerfd: Write a program that writes a heartbeat every two seconds and listens on stdin in the same loop using timerfd and poll.
- Counting lines in a big file with mmap: Map a file into memory with mmap and count its lines, behaving correctly for an empty file.
- Protecting a counter in shared memory: Add a POSIX semaphore to a program where two processes increment a counter in shared memory so the result is always right.
- Verifying the client on a UNIX socket: In a UNIX domain socket server, check the connecting user's UID with SO_PEERCRED and reject unauthorised ones.
- Dropping privileges in the right order: Fix privilege-dropping code that calls setuid before setgid, clear the supplementary groups as well and test that the dropped privileges cannot be regained.
- Watching a settings file for changes: Write a program that watches one settings file with inotify and prints a timestamped line when it changes.
- Watching new subfolders as well: Add automatic watching of newly created subfolders to a program that watches a folder tree.
- Copying a file under a storm of signals: Make the read and write calls of a process that keeps receiving signals survive EINTR and partial results and prove the copy's hash matches the source.
- Closing descriptors that leak into a child: Find the secret file and socket descriptors left open in a child started with exec through /proc, close them with O_CLOEXEC and verify only the three needed descriptors remain.
- Taking shared and exclusive file locks: Add a shared flock to a reader program and an exclusive one to a writer, and observe that two readers run together while the writer waits.
- Watching a file saved by rename: Fix a program that loses its watch when editors save through a temporary name and rename, by watching the directory and filtering the right events.
About this course
You work with files, processes, signals, pipes and shared memory directly through system calls and write services that shut down cleanly and drop privileges.
What you will learn
- System calls and files
- Processes
- Signals and time
- Memory and communication
- Capstone
Prerequisites
You should be comfortable with the level 101 and 201 topics of the same path.
Lab environment
Lessons run in your own Kali Linux terminal that opens in the browser. Nothing to install; the lab starts clean in every lesson.
Frequently asked questions
- Is this course right for my level?
- The course is at level 301; it assumes you know the topics of the lower-level courses.
- Is there anything to install?
- No. Every lesson runs in your own lab environment that opens in the browser.
- How do lessons work?
- Each lesson has 8 to 10 steps and a short quiz. Early steps show the command to type, later steps give less help; if two attempts fail, the solution opens.
- Can I try it for free?
- Yes. The first three lessons of every course are free.