Skip to content

Linux idle detection

David Anderson edited this page Apr 12, 2025 · 4 revisions

Problem

"Idle detection" means finding the time of the last user input (keystroke, mouse action), either of the user running BOINC or (preferably) all logged-in-users.

In 7.x and earlier Linux BOINC clients, idle detection is done in the client. It does this using several methods, including

  • looking at the access time of input devices
  • looking at the 'utmp' file
  • querying X11 servers

All of these are problematic for various reasons. Some of them don't work if the client is running as an unprivileged user.

Solution

The proposal is to do idle detection in an 'agent' outside the BOINC client. The agent may be a single process or a collection of processes, one of which communicates with the BOINC client. These processes may run as different users, such as the logged-in user or root.

The agent conveys the last-input time to the BOINC client. It does this through a memory-mapped file named /idle_detect_shmem. This is created with shm_open(), so it creates an entry in /dev/shm.

The shared-memory segment contains two Unix times, represented as unsigned 64-bit int (uint64_t):

  • the time the shared-mem segment was last updated
  • the time of the last user input

Typically the agent updates these once a second; that's the polling period of the BOINC client.

The last-update field acts as a "heartbeat": if it isn't being updated, the BOINC client knows that the agent isn't running. In this case it can notify the user, and either fall back on the old idle-detection code or assume that there is no user input.

Example code

This code shows how the agent can communicate with the client. This example simulates user input every 10 seconds.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <cstdint>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <time.h>

// idle detection sender example code.
// The shared-mem segment has two u64s, both updated once/sec
//
// 0: the current time
// 1: the time of last input
//
// 0) acts as a heartbeat.
// The BOINC client accepts 1) only if 0) is close to actual time;

int main(int, char**) {
    int ret;
    uint64_t x[2] = {0, 0};
    size_t sz = sizeof(x);

    // create shmem segment
    //
    int fd = shm_open("/idle_detect_shmem",  O_RDWR | O_CREAT, 0666);
    if (fd<0) {
        printf("open: %d\n", errno);
        exit(1);
    }

    // make it the right size
    //
    write(fd, x, sizeof(x));

    // memory-map it
    //
    uint64_t *p = (uint64_t*)mmap(NULL, sz, PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    if (p == MAP_FAILED) {
        printf("mmap: %d\n", errno);
        exit(1);
    }

    // simulate user input every 10 sec
    //
    int i = 0;
    while (true) {
        uint64_t now = (uint64_t)time(0);
        p[0] = now;
        if (++i == 10) {
            p[1] = now;
            i = 0;
        }
        sleep(1);
    }
}
Clone this wiki locally