Staff Software Engineer at Google Cloud, Applied AI | Linux and embedded systems by night | Efficient solutions to problems at every level of the tech stack

San Jose, CA
Joined May 2013
Lots of tutorials on using Linux: download a distro, how to install it... But how about: - Build the latest kernel from source - Adapt it to run on custom hardware ($5 hardware) - Make your own Linux board?! - Create USB gadgets with Linux - Hack your car! Follow me to learn!
39
94
4
1,582
Here's a Linux tip: When you kill a process, you're not "killing" it. You're sending it a signal. It's up to the process to decide what to do. kill <PID> (sends SIGTERM, signal 15): This is the "polite" way. It sends a SIGTERM (Terminate) signal. The process can "catch" this signal to clean up: save files, close connections, etc., then shut down gracefully. kill -9 <PID> (sends SIGKILL, signal 9): This is the "forceful" way. It sends a SIGKILL signal. This signal cannot be caught by the process. The kernel steps in and immediately terminates it. Consider trying kill first. Only use kill -9 if the process is stuck and refuses to die.
3
23
This thread is part of my ongoing "Linux System Call of the Day" series! Every day, I break down one of these syscalls, the vital API that lets your userspace programs ask the Linux kernel to perform powerful operations. Follow along to master the OS from the ground up!
1
16
When to use which? - lstat(): When you need to know about the file system entry itself (ls, backup tools, file managers) - stat(): When you want to work with the actual file content (editors, compilers) Many security vulnerabilities come from using the wrong one. Always think: "Do I want the link or the target?"
1
17
The solution? Use lstat() when you need to inspect the link itself: lstat() is stat()'s cautious sibling. It does the exact same thing, with one critical difference: It DOES NOT follow symlinks. ❌➡️ If lstat() sees a symlink, it reports the link's metadata (type is S_IFLNK, size is the path length in bytes). Tools like ls, find, and tar use lstat() by default for this reason.
1
18
Here's code that falls into the trap: #include <stdio.h> #include <sys/stat.h> int main(int argc, char **argv) { struct stat sb; // This follows symlinks! if (stat(argv[1], &sb) == -1) { perror("stat failed"); return 1; } // For a symlink, this shows the TARGET's size printf("Size: %lld bytes\n", (long long) sb.st_size); return 0; } This code can't tell if argv[1] is a symlink. It always sees the destination.
1
14
🚨 THE TRAP 🚨 What if the path is a symbolic link? $ ln -s /usr/bin/python my_link If you call stat("my_link", &sb), it will SILENTLY FOLLOW the link. You'll get the size and permissions of /usr/bin/python, not my_link itself! This is why ls -l wisely uses lstat() instead. It needs to show you the link, not what it points to.
1
26
The stat() syscall (#4 on x86_64) and lstat() (#6) are your direct line to a file's inode, its metadata. You give them a path, and the kernel returns a struct stat packed with info: ✅ File type ✅ Permissions (st_mode) ✅ Size (st_size) ✅ Timestamps But there's a critical difference between them...
1
22
You run ls -l. It shows a file's size, permissions, and timestamps. How? It's not reading the whole file. It's using the lstat() syscall. ⚡️ But wait: why lstat() and not stat()? Because stat() has a massive "gotcha" that traps new systems programmers all the time. A thread on the twin syscalls that read metadata... and their deceptive difference. 🧵👇
2
9
162
Uros Popovic retweeted
Managed to get WiFi working on Linux with an unsupported WiFi card! (MT7902) By using QEMU/KVM with PCIe passthrough and Internet sharing on the Windows VM to the tap0 interface. You can get the WiFI connection to work on the host through this hacky method!
Move forward in your Linux journey beyond distro hopping and ricing! Start building your own PCBs, for example! Follow me for more deep dives like this and check out my website at popovicu.com!
2
3
Here's the fabricated final product, my first Linux PCB!
2
8
Designing a custom PCB that boots Linux is easier than you think. I recently went from zero PCB experience to holding my own fabricated board that boots a mainline Linux kernel. You don't need to be an expert to start. The key is a simple design. I used an Allwinner SoC with on-chip RAM (which avoids complex memory routing) and a basic flash chip. Modern tools like EasyEDA integrate directly with manufacturers like JLCPCB, letting you pick from a library of standard components that they will source and assemble for you. You can go from a schematic to a fabricated board that boots Linux surprisingly quickly. It won't be professional and production-ready, but for something that just boots Linux, it's easy to get started. Here's the full write-up of my journey, showing every step from idea to a booting system: popovicu.com/posts/making-my…
1
5
40
Uros Popovic retweeted
For performance, the Linux kernel buffers writes in memory. An error from a previous `write()` (e.g., disk full, quota exceeded) might not be reported immediately. The error is often saved and only returned when you finally call `close()`! If you don't check `close()`'s return value, you get silent data loss.
2
1
80
You don't need a special driver to control a USB device in Linux. You just need to know which file to read. It may seem that if there's no driver, the device is a paperweight. But Linux is smarter than that. It gives you a 'superpower' - a raw file that lets you speak directly to the hardware. This is /dev/hidraw0. I just used this one file to reverse-engineer my new UPS. Here's the "secret" playbook: 1. Find the "Manual": Linux already saved the device's "instruction manual" for you. It's a binary file: /sys/class/hidraw/hidraw0/device/report_descriptor. 2. Decode the "Manual": The file is a cryptic "script". I fed it to an AI with context (a GitHub data.py file) and it instantly decoded the entire protocol, revealing all the data reports. It's possible to go through this manually, though somewhat time consuming. 3. Use the Right Tools: The device uses two types of reports. Linux gives you a simple tool for each: * Auto-sent data (battery %): Read with the read() system call. * Polled data (load %): Request with the ioctl() system call. 4. Build the Program: I put it all together in a minimal C program that can now monitor my UPS 24/7 on an old Raspberry Pi. No 3rd-party drivers, no magic. Just reading and writing to files. That's the power of the Linux philosophy. Want to learn how to do this yourself? The full deep-dive, from decoding the "script" to the final C code, is in my new blog post. popovicu.com/posts/how-to-re…
16
104
5
1,202
This thread is part of my ongoing "Linux System Call of the Day" series! Every day, I break down one of these syscalls - the vital API that lets your userspace programs ask the Linux kernel to perform powerful operations. Follow along to master the OS from the ground up!
2
54
So what's the solution for guaranteed writes? 1. Always check `close()`'s return value for logging and diagnostics. 2. If you absolutely need data flushed to the storage before proceeding, call `fsync(fd)` *before* you call `close(fd)`. `fsync()` blocks until the data is flushed, giving you a much safer guarantee.
3
2
92
But here's the *real* trap: If `close(fd)` fails with an error like `EIO`, NEVER retry it! The kernel releases the file descriptor number `fd` for reuse *before* attempting to flush the data. If you call `close(fd)` again, you might accidentally close a completely different file just opened by another thread.
1
1
60
Always check the return value of `close()`. It's not just for cleanup; it's your last chance to catch write errors. Here’s the right way to handle it: #include <unistd.h> #include <fcntl.h> #include <stdio.h> // for perror int main() { int fd = open("data.txt", O_WRONLY | O_CREAT, 0644); // ... imagine a write() call here ... write(fd, "important data", 14); if (close(fd) == -1) { perror("close failed"); // CRITICAL! Data may be lost. } return 0; }
1
1
67
Your program writes to a file. `write()` succeeds. `close()` returns 0. So your data is safely on disk, right? 🤨 Not so fast. This is one of the most common and dangerous traps in Linux I/O. A thread on silent data loss and the `close()` syscall (#3 on x86_64). 🧵👇