ADDED bgrd.c Index: bgrd.c ================================================================== --- bgrd.c +++ bgrd.c @@ -0,0 +1,180 @@ +/* [ʞ] bgrd.c + * ~ lexi hale + * $ cc -Ofast bgread.c -lutil -obgrd + * © affero general public license 3.0 + + * i am angry beyond words that this program had to + * be written + * + * bgrd "background read" is a tool for launching an + * application and retrieving the first line of output. + * this is a nontrivial task for a number of reasons, + * all of which are incredibly stupid, however, most + * saliently: buffered io. + * + * the suckmore web browser `surf` has an option '-x' + * which is intended to print the X window id of the + * window it opens, so it can be captured and used in + * scripts. so far so good. + * + * except unix has two distinctly different concepts + * of IO. there's POSIX IO, and then there's libc IO. + * + * POSIX IO uses the shit in and ; + * syscalls like read(2), write(2), and pipe(2) - the + * good, simple shit God made unix for. this is really + * bare-metal; these are basically C wrappers over + * kernel syscalls. POSIX IO uses plain old ints as + * file descriptors, and it doesn't fuck around. when + * you say "write," god dammit, it WRITES. + * + * libc is a very different beast. libc has opinions. + * libc has abstractions. libc has its own entire + * goddamn DSL by which to specify format strings, + * because apparently someone felt called to reinvent + * FORTRAN except worse. printf(), you know, the first + * function they ever teach you in C 101? (more like CS + * 403 these days, but aghhh) it's actually a heinously + * complicated, dangerous, slow, insecure mess that drags + * a ridiculous amount of background bullshit into the + * frame. such as BUFFERING. + * + * libc, you see, is too good to just wrap write() and + * read(). no, it also has to decide when to SEND them. + * this is responsible for that behavior every c coder + * trips over eventually - you know the one, where if + * you don't end your format string in '\n' it isn't + * printed, sometimes even if the program exits. this + * is not actually a POSIX thing; it's a libc thing. + * + * libc has a couple different kinds of buffering tactics + * (set with setvbuf(), a function nobody seems to know + * exists) that it uses in different circumstances. + * the printf \n gotcha behavior is what's known as + * "line buffering." however, because libc thinks it's + * fucking smart or something, it's not content to just + * pick one predictable behavior and stick to it. oh no. + * + * ever noticed how programs can seem to tell whether + * they're connected to a terminal (and thus can output + * all those fancy ansi formatting codes), or whether + * you're redirecting their stdout to a file? that's + * because there's more than one kind of pipe. the kind + * you create with pipe(2) - and the kind you create with + * openpty(2). + * + * a pty is, essentially, a kind of pipe that carries + * extra information around, the information you access + * via ioctl and termios. it's designed to imitate a TTY, + * so that a shell can create one, aim a process at it, + * and then that process can draw TUIs and shit on it. + * and some programs are designed to behave differently + * depending on whether they're hooked up to a pipe or a + * pty. + * + * libc, tragically, is among them. + * + * if libc notices it's hooked up to a pipe instead of a pty, + * it will change its default buffering strategy. newlines + * will suddenly cease flushing stdout, and libc will + * only print its buffer in one of two circumstances: the + * buffer is filled up, or the program exits. + * + * this is a problem if you are, say, trying to output a + * handle that scripts can use to control the running + * program. + * + * the `surf` developers had a couple of options. they + * could have simply broken out the POSIX headers and + * sent the X id to stdout with a call to write(2), the + * correct thing to do. they could have thrown in a call + * to setvbuf(3) to explicitly pick a buffering strategy + * compatible with their usecase, the sensibly wrong + * thing to do. they could have explicitly flushed stdout + * after printf(3)'ing to it, the dumb and error-pront + * thing to do. + * + * instead, they did *nothing.* + * + * so if you run `surf -x` from a terminal, great! + * you'll see it print the x window id first thing. + * you'll then try to capture it via any number of + * increasing desperate means, all of which will fail + * hilariously. finally, you'll spend four goddamn hours + * after midnight reading source code and manpages and + * frantically googling around and digging up unix lore + * and finally, FINALLY figure out what the batshit FUCK + * is going on, and write this goddamn utility to hack + * around the suckmore crowd's suckitude. + * + * so i figured i'd save you some time. + * + * i am probably going to submit a PR eventually because + * holy hell this is just so monumentally bozotic. + * + * in the mean time, you can use this extremely + * no-bullshit wrapper by running `set surfwin (bgrd + * (which surf) surf -x ` or whatever the bash + * equivalent is and it will immediately launch surf in + * the background, printing the X window and exiting + * as soon as it hits a newline. it should be adaptable + * to similar scenarios if you find yourself dealing with + * similarly broken software tho. + * + * in conclusion, read lenin. */ + +#include +#include +#include +#include +#include + +int main(int argc, char** argv) { + // pipe fd handles + int wr, rd; + + // populate the pipe + openpty(&wr, &rd, NULL, NULL, NULL); + + // note ptys and pipes use basically the same + // interface; you can use one as a drop-in + // replacement for the other, tho the creation + // function syntax is a bit different. + + if(fork()) goto master; + else goto child; + + master: { + close(wr); + char buf[256]; size_t sz; + while(sz = read(rd, buf, 255)) { + buf[sz]=0; + for (size_t i=0;i * ~ lexi hale * $ cc -Ofast safekill.c -lX11 -lc -osafekill * © affero general public license + * a utility to make it harder to accidentally nuke * important windows on i3wm or whatever. */ #include #include