flak rss random

a grep that doesn't stuck

Sometimes pipes get stuck. To recap, if we run this command...

~/src/usr.bin/grep> tail -200 util.c | grep unsigned | grep int
grep_revstr(unsigned char *str, int len)

... we get the expected output. If we run it this way, however, there’s no output.

~/src/usr.bin/grep> tail -f -200 util.c | grep unsigned | grep int

The file isn’t being appended, meaning tail won’t exit, so we don’t expect the pipeline to ever finish, but we would like to see the current results.

Fortunately, grep provides an lbflag we can set. In this case, if the input is a pipe, we’ll assume there’s something fancy going on, and switch to line buffering.

        struct stat sb;
        fstat(0, &sb);
        if ((sb.st_mode & S_IFMT) == S_IFIFO)
                lbflag = 1;

Easy fix. (Or one can read the manual.)

But this is excessive. What about those times when we want the efficiency of buffering? There are lots of possible pipelines that don’t involve tail. What we can do instead is take a peek at some of our neighbors and see what’s really going on. Not perfectly accurate, but probably good enough.

static void
checklbflag()
{
        pid_t pgrp = getpgrp();
        struct kinfo_proc ki[24];
        size_t len = sizeof(ki);
        int mib[6] = { CTL_KERN, KERN_PROC, KERN_PROC_PGRP, pgrp,
                sizeof(ki[0]), sizeof(ki)/sizeof(ki[0]) };

        sysctl(mib, 6, ki, &len, NULL, 0);
        for (size_t i = 0; i < len / sizeof(ki[0]); i++)
                if (strcmp(ki[i].p_comm, "tail") == 0)
                        lbflag = 1;
}

Alas, this requires including <sys/sysctl.h> which will slow down our compiles.

It is also possible, using forbidden magic, to determine whether the pipe we are reading from is being written to by tail, as opposed to a tail at the end of the pipeline, but that’s left as an exercise for the reader.

Posted 10 Jan 2025 14:19 by tedu Updated: 10 Jan 2025 14:19
Tagged: c openbsd programming