#Stream stdout from `process.Child`

1 messages · Page 1 of 1 (latest)

near bobcat
#

bluetoothctl scan on will stream a series of local bluetooth devices to stdout. Is there a way for me to stream this input in Zig? For example, I assume I'd want to run a separate thread running this command and streaming stdout, splitting the results into a device list and having the main programming doing something and outputting back to the user.

I currently have a small example for spawning the child process but it seems to wait for all output to complete which isn't too helpful. any advice is greatly appreciated.

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var child = std.process.Child.init(&.{ "bluetoothctl", "scan", "on" }, allocator);
    child.stdout_behavior = .Pipe;
    child.stderr_behavior = .Pipe;

    var stdout = std.ArrayList(u8).init(allocator);
    var stderr = std.ArrayList(u8).init(allocator);
    errdefer {
        stdout.deinit();
        stderr.deinit();
    }

    try child.spawn();
    try child.collectOutput(&stdout, &stderr, 4096);

    const out = try stdout.toOwnedSlice();
    defer allocator.free(out);

    std.debug.print("[DEBUG] {s}\n", .{out});

    child = std.process.Child.init(&.{ "bluetoothctl", "scan", "off" }, allocator);
    try child.spawn();
    _ = try child.wait();
}
near bobcat
#

@short shard sorry to keep pinging you for this 🥲, even using poll seems to cause it to hang for a bit, or am I doing something wrong here to cause it to hang?

short shard
#

this is the one i use

fn io_ready(fd: std.os.fd_t, timeout_ms: c_int) !bool {
    var polls = [_]std.os.linux.pollfd{
        .{
            .fd = fd,
            .events = std.os.linux.POLL.IN,
            .revents = undefined,
        },
    };
    const ready_len = try std.os.poll(&polls, timeout_ms);
    if (ready_len == 1) return true;
    if (ready_len == 0) return false;
    unreachable;
}
#

and then make sure u use read instead of readAll when collecting

near bobcat
short shard
#

no output is collected in that function

#

u do it after

#

something like this but with the type changed

fn collect_sock(sock: network.Socket) ![]const u8 {
    var list = std.ArrayListUnmanaged(u8){};
    errdefer list.deinit(alloc);

    while (try io_ready(sock.internal, std.time.ms_per_s)) {
        var buf: [1024]u8 = undefined;
        const len = try sock.reader().read(&buf);
        try list.appendSlice(alloc, buf[0..len]);
    }
    return list.items;
}
near bobcat
#

ohhhhhh, that makes sense! thanks for that

near bobcat
#

@short shard maybe I'm mistaken but is it not something like this?

short shard
#

yes but there can also be a race condition if there's also stderr filling the buffer and not being discarded

#

idk if race is the right word per se but it can cause blockage

#

i wouldve assumed they'd have a different buffer but i ran into this issue a long time ago during a zig showtime episode once

near bobcat
short shard
#

it doesnt run at all or it only runs once?

near bobcat
#

doesn't seem to run at all

short shard
#

whats bluetoothctl scan on look like if u run that directly?

near bobcat
#

it just outputs to stdout as it finds devices

short shard
#

the while(io_read(... loop will only collect a line maybe two at a time, so you'll need an outer loop to make it continuously scan for new output

#

that can be another while or more integrated into the main loop of your program

near bobcat
#

hmmm, still not having fun it seems lol

short shard
#

i wouldnt print beginning and end

tropic coyote
#

I wonder if bluetoothctl scan on only prints if stdout is a tty

#

What happens if you redirect it into a file?

near bobcat