Skip to content

sane_close/sane_exit doesn't free all used pipe/timer_fd if we loop use sane_close /sane_ext without exiting program.

Sample code as below to loop use sane api without exiting program will lead to too many opened files error.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sane/sane.h>
#include <sane/saneopts.h>

void print_option_values(SANE_Handle device) {
    SANE_Int num_options;
    SANE_Status status;

    // Get the number of options available for the device
    status = sane_control_option(device, 0, SANE_ACTION_GET_VALUE, &num_options, NULL);
    if (status != SANE_STATUS_GOOD) {
        fprintf(stderr, "Failed to get number of options: %s\n", sane_strstatus(status));
        return;
    }

    printf("Device has %d options:\n", num_options);

    // Iterate through all options
    for (SANE_Int opt = 1; opt < num_options; opt++) {
        const SANE_Option_Descriptor *desc = sane_get_option_descriptor(device, opt);
        if (!desc) {
            fprintf(stderr, "Option %d has no descriptor.\n", opt);
            continue;
        }

        printf("Option %d: %s (%s)\n", opt, desc->name ? desc->name : "Unnamed", desc->title ? desc->title : "No title");

        if (!(desc->cap & SANE_CAP_INACTIVE)) {  // Skip inactive options
            char value_buffer[1024] = {0};  // Adjust buffer size as needed
            status = sane_control_option(device, opt, SANE_ACTION_GET_VALUE, value_buffer, NULL);
            if (status == SANE_STATUS_GOOD) {
                switch (desc->type) {
                    case SANE_TYPE_BOOL:
                        printf("  Value: %s\n", (*(SANE_Bool *)value_buffer) ? "True" : "False");
                        break;
                    case SANE_TYPE_INT:
                        printf("  Value: %d\n", *(SANE_Int *)value_buffer);
                        break;
                    case SANE_TYPE_FIXED:
                        printf("  Value: %.2f\n", SANE_UNFIX(*(SANE_Fixed *)value_buffer));
                        break;
                    case SANE_TYPE_STRING:
                        printf("  Value: %s\n", value_buffer);
                        break;
                    default:
                        printf("  Value: Unknown type\n");
                        break;
                }
            } else {
                fprintf(stderr, "  Failed to get value: %s\n", sane_strstatus(status));
            }
        } else {
            printf("  Option is inactive.\n");
        }
    }
}

int main() {
    for (int iteration = 0; iteration < 1000; iteration++) {  // Replace with a suitable termination condition
        printf("\nIteration %d: Initializing SANE...\n", iteration + 1);

        SANE_Status status;
        SANE_Int version_code;
        const SANE_Device **device_list;

        // Initialize SANE
        status = sane_init(&version_code, NULL);
        if (status != SANE_STATUS_GOOD) {
            fprintf(stderr, "Failed to initialize SANE: %s\n", sane_strstatus(status));
            return 1;
        }
        printf("SANE initialized. Version: %d.%d.%d\n",
               SANE_VERSION_MAJOR(version_code),
               SANE_VERSION_MINOR(version_code),
               SANE_VERSION_BUILD(version_code));

        // Get the list of devices
        status = sane_get_devices(&device_list, SANE_FALSE);
        if (status != SANE_STATUS_GOOD) {
            fprintf(stderr, "Failed to get device list: %s\n", sane_strstatus(status));
            sane_exit();
            sleep(1);
            continue;  // Retry in the next iteration
        }

        if (!device_list || !device_list[0]) {
            fprintf(stderr, "No devices found.\n");
            sane_exit();
            sleep(5);
            continue;  // Retry in the next iteration
        }

        printf("Using device: %s (%s)\n", device_list[0]->name, device_list[0]->vendor);

        // Open the first available device
        SANE_Handle device;
        status = sane_open(device_list[0]->name, &device);
        if (status != SANE_STATUS_GOOD) {
            fprintf(stderr, "Failed to open device: %s\n", sane_strstatus(status));
            sane_exit();
            sleep(5);
            continue;  // Retry in the next iteration
        }

        // Fetch and print all option values
        print_option_values(device);

        // Close the device and cleanup
        sane_close(device);
        sane_exit();

        // Wait for 5 seconds before the next iteration
        sleep(5);
    }

    return 0;
}

image

Check program with lsof will find that a lot new pipe/timefd related fp added.

image

Did we free all used resources on sane_close/sane_exit which is added mainly around sane_get_devices api.

Edited by darren qiao
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information