crates/veilvoice-watch/src/linux.rs
veilvoice-watch · 201 lines · read the source here · or on GitHub
Linux detection, via open file handles in /proc.
How it works
A process using the microphone has a file descriptor open on an ALSA PCM capture node, /dev/snd/pcmC0D0c, where the trailing c means capture as opposed to p for playback. A process using the camera has one open on /dev/video*. Walking /proc/*/fd and resolving the symlinks finds them, along with the PID and the process name, with no dependency and no daemon.
Capture and playback are distinguished deliberately. Treating every open /dev/snd handle as microphone use would report a music player as listening to you, and a monitor that cries wolf gets ignored, which is the worst possible outcome for this feature.
Sound servers
On most desktops PipeWire or PulseAudio owns the hardware, so the process holding the PCM node is the server, not the application behind it. That is reported honestly rather than hidden: the server appearing means something is capturing, and where the client can be identified from the ALSA /proc/asound bookkeeping, it is named too.
The permission boundary
/proc/<pid>/fd is readable only by the process owner and root. Without root you therefore see your own processes; another user's are invisible. That is a kernel boundary, not a gap in this code, and crate::support says so rather than letting an empty list imply an empty machine.
In plain words
Finds out which programs are using the microphone or camera on Linux, by looking at which of them have the device open.
That is exactly what the system already knows and nothing has to be installed to ask. It sees what your own account can see, so something running as another user may not appear, and an empty list is not proof of a quiet machine.
WHAT THIS FILE CONTAINS
201 lines defining 5 functions (1 public), 0 types and 0 constants. Everything below is read out of the source, so it cannot disagree with the code.
What happens when it runs. These are the ways in: public, and nothing else in this file calls them, so they are what an outside caller reaches first.
scanline 46
reachesclassify,process_name,started_at
WHAT CALLS WHAT
The functions this file defines, and the calls between them. An edge means the callee's name appears, called, inside the caller's body. This is a syntactic reading, not a type-resolved one.
The same graph as Mermaid source
%%{init: {"theme":"base","themeVariables":{"background":"#1a1b26","primaryColor":"#1f2335","primaryTextColor":"#c0caf5","primaryBorderColor":"#7aa2f7","secondaryColor":"#16161e","tertiaryColor":"#16161e","lineColor":"#737aa2","textColor":"#c0caf5","mainBkg":"#1f2335","nodeBorder":"#7aa2f7","clusterBkg":"#16161e","clusterBorder":"#2f3549","fontFamily":"ui-monospace, SFMono-Regular, Consolas, monospace","fontSize":"14px"}}}%%
flowchart TD
n_scan(["scan<br/>line 46"])
n_classify["classify<br/>line 103"]
n_process_name["process_name<br/>line 124"]
n_started_at["started_at<br/>line 135"]
n_approx_now_minus["approx_now_minus<br/>line 141"]
n_scan --> n_classify
n_scan --> n_process_name
n_scan --> n_started_at
click n_scan href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-watch/src/linux.rs#L46" "open the source"
click n_classify href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-watch/src/linux.rs#L103" "open the source"
click n_process_name href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-watch/src/linux.rs#L124" "open the source"
click n_started_at href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-watch/src/linux.rs#L135" "open the source"
click n_approx_now_minus href "https://github.com/tilas01/veilvoice/blob/main/crates/veilvoice-watch/src/linux.rs#L141" "open the source"
classDef entry fill:#1f2335,stroke:#7aa2f7,color:#c0caf5
class n_scan entry
classDef helper fill:#1f2335,stroke:#bb9af7,color:#c0caf5
class n_classify,n_process_name,n_started_at,n_approx_now_minus helper
This site loads no third-party script, so it cannot run Mermaid; the diagram above is the same nodes and edges drawn by the generator instead. GitHub renders the source below directly.
ITEMS
| Item | Line | Documentation |
|---|---|---|
scan pub fn | 46 | |
classify fn | 103 | Decide whether an open handle means capture. |
process_name fn | 124 | |
started_at fn | 135 | When the process started, from the modification time of its /proc entry. |
approx_now_minus fn | 141 | Unused today; kept because a future PipeWire client lookup will want it. |