Index: bgrd.c ================================================================== --- bgrd.c +++ bgrd.c @@ -1,126 +1,127 @@ /* [ʞ] bgrd.c * ~ lexi hale * $ cc -Ofast bgrd.c -lutil -obgrd * © affero general public license 3.0 - * i am angry beyond words that this program had to - * be written + * 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 + * 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 + * 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. + * 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. + * 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 + * 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 + * 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, 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. + * (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 + * 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 + * 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 + * 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. + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 + * 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 ADDED smake.c Index: smake.c ================================================================== --- smake.c +++ smake.c @@ -0,0 +1,193 @@ +/* [ʞ] smake.c + * ~ lexi hale + * $ cc -Ofast smake.c -osmake + * © affero general public license + + * I N C O M P L E T E * + | basic functionality is still | + | heavily under development | + + * a replacement for sassc --watch, smake maintains an + * in-memory dependency graph for the specified files + * and automatically re-compiles them when they or a + * dependency of theirs changes + + TODO add switch to generate makefile from constructed + graph and exit */ + +#include +#include +#include +#include +#include +#include + +enum e { ok, badname, nofile }; +enum kind { css, sass, scss, bad }; +typedef enum { false, true } bool; + +#define corebufsz 1024 +char corebuf[corebufsz]; +void* extbuf, * bufptr; +size_t run = 0; +#define max(a,b) (a > b ? a : b) + +void* alloc(size_t sz) { + if((bufptr + sz) < ((void*)corebuf + corebufsz)) goto mkptr; + else { + if (run == 0) { + run = corebufsz; + extbuf = malloc(run); + bufptr = extbuf + sz; + return extbuf; + } else { + if ((bufptr + sz) < (extbuf + run)) goto mkptr; + else { + run += max(512, sz+128); + size_t rsz = bufptr-extbuf; + extbuf = realloc(extbuf, run); + bufptr = extbuf + rsz + sz; + return bufptr - sz; + } + } + } + // this point should never be reached + mkptr: { + void* ret = bufptr; + bufptr += sz; + return ret; + } +} + + + +struct file { + char* src; + char* out; + enum kind kind; + struct dep* deplink; +}; + +struct dep { struct file* f; struct dep* nextdep; }; + +void readfile(struct file* f, char* src) { + f->src = src; + + int fd = open(src, O_RDONLY); + int sz = lseek(fd,0,SEEK_END); lseek(fd,0,SEEK_SET); + char* file = mmap(0,sz,PROT_READ,MAP_PRIVATE,fd,0); + close(fd); + + char namebuf[256]; + char* namecur = namebuf; + char* cur = file; + + char strqt = 0; + +read_start: + if (*cur == 0) goto read_done; + if (*cur == '"') { ++ cur; strqt = '"'; goto read_string; } + if (*cur == '\'') { ++ cur; strqt = '\''; goto read_string; } + if (*cur == '(') { ++ cur; strqt = ')'; goto read_string; } + if (*cur == '[') { ++ cur; strqt = ']'; goto read_string; } + if (*cur == '/') { + switch(cur[1]) { + case '/': goto skip_line; + case '*': cur += 2; goto read_ml_comment; + case 0: goto read_done; + } + ++cur; goto read_start; + } + if (*cur == '@') { + if (strncmp(cur+1, "import", 6) == 0) { + cur += 7; goto read_import; + } + } + ++cur; goto read_start; + +read_import: + if (*cur == ' ' || *cur == '\t' || *cur == '\n') { + ++cur; goto read_import; + } else if (*cur == 'u' && cur[1] == 'r' && cur[2] == 'l') { + strqt=')'; goto read_string; + } else if (*cur == '"' || *cur=='\'') { + strqt=*cur; + while(*++cur != strqt) { + if (*cur == '\\' && cur[1] == strqt) { + *namecur++=strqt; cur += 2; + // FIXME support other escapes + } else { + *namecur++=*cur; + } + } + *namecur = 0; + printf("found import to %s;\n", namebuf); + namecur = namebuf; + } + ++cur; goto read_start; + +read_string: + if (*cur == 0) goto read_done; //unterminated string!? + if (*cur++ == strqt) goto read_start; + goto read_string; + +read_ml_comment: + if (*cur == 0) goto read_done; //unterminated comment! + if (*cur == '*' && cur[1] == '/') { + cur += 2; + goto read_start; + } + ++cur; goto read_ml_comment; + +skip_line: + if (*cur == 0) goto read_done; + if (*cur++ == '\n') goto read_start; + goto skip_line; + +read_done: + + munmap(file,sz); +} + +enum e parsename(struct file* f) { + char* ext = NULL; + char* path = f->src; + + while(*path++!=0) { + if(*path == '.') ext = path + 1; + } + + + if(ext == NULL) return badname; + + // quickly determine file type + if (ext[0] == 's' + && ext[2] == 's' + && ext[3] == 's') { + if (ext[1] == 'c') f -> kind = scss; + else if (ext[1] == 'a') f -> kind = sass; + else f -> kind = bad; + } else if (ext[0] == 'c' + && ext[1] == 's' + && ext[2] == 's' ) f -> kind = css; + else f -> kind = bad; + + if(f -> kind == sass || f -> kind == scss) { + size_t sz = (ext - f->src); + f->out = alloc(sz + 4); + strncpy(f->out, f->src, sz); + f->out[sz+0] = 'c'; + f->out[sz+1] = 's'; + f->out[sz+2] = 's'; + f->out[sz+3] = 0; + } else f -> out = f -> src; + + printf("src %s; dst %s; ext %s;\n", f->src,f->out,ext); +} + +int main(int argc, char** argv) { + bufptr = corebuf; + struct file top; + readfile(&top, argv[1]); + parsename(&top); +} ADDED xpriv.c Index: xpriv.c ================================================================== --- xpriv.c +++ xpriv.c @@ -0,0 +1,341 @@ +/* [ʞ] xpriv.c + * ~ lexi hale + * $ cc -Ofast xpriv.c -lrt -lutil -lX11 -oxpriv + * © affero general public license + + * xpriv.c is a tool for a very specific use case i have. + * for security's sake, i don't tie my ssh keys to my + * login session. when i intend to use them, i spawn a + * special terminal and load the keys into memory, then + * exit that terminal when i'm done using them. however, + * i often lose track of or accidentally kill that win- + * dow, despite adding a visual cue to its prompt. + * safekill.c was one half of my attempt to address this + * problem; this is the other. + * + * xpriv performs several different tasks to accomplish a + * single purpose: i can hit a single keystroke that + * will either conjure up a new privileged session, or + * switch to one that's already active if it exists. it + * does this by first checking for the existence of a + * shared memory segment. if it doesn't find it, it + * starts a new session; if it *does* find it, it + * retrieves the X11 window ID from that shared memory + * and sends a _NET_ACTIVE_WINDOW client message to the + * root X window. the window manager interprets message, + * activating the window. + * + * the flag -k can also be passed, in which case the + * utility instructs the running process to liquidate its + * subprocesses and exit itself. + * + * if the shared memory does not exist, xpriv creates a + * new instance of urxvt. this instance is told to run + * the command “xpriv -a” instead of the user’s normal + * shell. the -a flag instructs xpriv to get the terminal + * window’s ID from the $WINDOWID environment variable + * which urxvt sets. after this, a ssh-agent process is + * launched. spriv waits until it has opened a socket and + * then runs ssh-add without parameters to add the user's + * default keys to the session. + * + * xpriv does its best to clean up after itself, + * killing all sensitive processes and their children and + * removing the shmem segment when it is no longer in + * use, even if exits somewhat abnormally. if you have to + * kill -9 xpriv at any point tho, you can make it work + * again (on linux) with + * rm /dev/shm/k.xpriv:(xpriv binary basename) + * + * you can have multiple xpriv sessions by creating soft- + * links to the binary with a different name for each. + + TODO send signal to urxvtd directly instead of launching + urxvtc as a separate process + + TODO make shell & commands performed configurable, with + flags to control or supplant ssh-agent + + TODO add a randomizer call that works on BSD + + TODO document flags + + TODO implement/remove lock flag + + TODO add flag to bring window to current desktop */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //TODO bsd compat + +#define shmem_prefix "/k.xpriv:" + +typedef enum { false, true } bool; +enum mode { mode_usage, mode_go, mode_register, + mode_kill, mode_lock }; + +enum res { ok, fail_parse, fail_arg, fail_opt, fail_shm, + fail_mem, fail_pty, fail_nop, fail_win, + fail_wid, fail_x11}; +enum res bad(enum res code) { + if (code == ok) return ok; + + write(1,"\e[1m|e|\e[0m ",12); + const char* msg; uint8_t len; + switch(code) { + #define say(x) { msg = (x "\n"); len = (sizeof x) + 1; break; } + case fail_parse: say("could not parse command-line options; pass -u for usage"); + case fail_arg : say("invalid argument provided"); + case fail_opt : say("no such option"); + case fail_shm : say("instance already running"); + case fail_mem : say("could not map memory"); + case fail_pty : say("could not alloc pty"); + case fail_nop : say("no instance running"); + case fail_win : say("cannot open display"); + case fail_wid : say("WINDOWID not defined"); + case fail_x11 : say("X server refuses to handle request"); + #undef say + } + write(1, msg, len); + return code; +} + +struct signal { + Window wid; + pid_t pid; + pid_t agent; + enum mode op; +}*global; + +char* itoa(unsigned long i, char* buf, size_t bufsz) { + char* cur = buf + bufsz; + while (cur >= buf) { + *--cur='0' + i % 10; + if (i < 10) break; else i /= 10; + } + return cur; +} + +bool run; + +struct termios initial_state; + +void sigusr(int a) { if (global -> op == mode_kill) run = false; } +void sigterm(int a) { run = false; } + +enum res register_window(const char* id, bool weak) { + // the id field denotes the path to the shared memory + // in use, and allows the user to have multiple + // contexts by creating aliases to the binary + int fd = shm_open(id, O_CREAT | O_EXCL | O_RDWR, 0600); + ftruncate(fd, sizeof(struct signal)); + if (fd == -1) return fail_shm; + + struct signal* s = mmap(0, sizeof(struct signal), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if(s == MAP_FAILED) return fail_mem; + else global = s; + + Display* xdpy = XOpenDisplay(NULL); + Atom xvital; + + /* x11 */ { + if (xdpy == NULL) return fail_win; + Window win; + const char* winid_s; + if (!(winid_s = getenv("WINDOWID"))) return fail_wid; + win=(Window)strtol(winid_s,NULL,10); + if(weak == false) xvital = XInternAtom(xdpy,"_k_vital",false); + s -> wid = win; + } + + pid_t child; + if(child = fork()) { + s -> pid = child; + sigset_t mask, oldmask; + sigemptyset(&mask); + sigaddset(&mask, SIGUSR1); + sigprocmask(SIG_BLOCK, &mask, &oldmask); + + signal(SIGUSR1, sigusr); + signal(SIGTERM, sigterm); + signal(SIGCHLD, sigterm); + signal(SIGHUP, sigterm); + + run = true; + while(run == true) { + sigsuspend(&oldmask); + } + + if(s -> op == mode_kill) { + kill(s -> pid, SIGTERM); + } + kill(s -> agent, SIGTERM); + sigprocmask(SIG_BLOCK, &mask, NULL); + shm_unlink(id); + if (!weak) { + XDeleteProperty(xdpy,s -> wid,xvital); + XSync(xdpy,false); + } + XCloseDisplay(xdpy); + } else { + // now we start ssh-agent and set the proper environment + // variables + pid_t ssha; + + /* messy part */ { + const char* tmp; //tmpdir defined? + if (!(tmp = getenv("TMPDIR"))) tmp = "/tmp"; + size_t tmpsz = strlen(tmp); + + char sockn[tmpsz + 1 + sizeof "ssh." + + 11]; + strcpy(sockn, tmp); + sockn[tmpsz] = '/'; + strcpy(sockn+tmpsz+1,"ssh."); + + // first, gen a random identifier so we have the ability + // to know where the socket winds up + uint8_t* rndid = sockn+tmpsz+1+4; + getrandom(rndid, 11, 0); + + // assuming ascii… + for(uint8_t*i=rndid;i '9') *i += 7; + if (*i > 'Z') *i += ('a' - 'Z'); + } + + if (ssha = fork()) { + char pid_s_buf[16]; + char* pid_s = itoa(ssha, pid_s_buf, sizeof(pid_s_buf)); + while (access(sockn, F_OK)); + // avoid nasty race condition + setenv("SSH_AGENT_PID", pid_s, true); + setenv("SSH_AUTH_SOCK", sockn, true); + s -> agent = ssha; + } else { + close(1); close(0); + execlp("ssh-agent","ssh-agent","-D", "-a",sockn,0); + } + } + + pid_t sad; + int p; + if (sad = fork()) { + int added; + waitpid(sad, &added, 0); + if (added == 0) { + if (weak == false) { + XChangeProperty(xdpy,s -> wid,xvital,xvital,8,PropModeReplace,"\01", 1); + XSync(xdpy,false); + } + write(1,"\033c",3); + execlp("fish","fish",NULL); + } else { return ok; } + } else { + execlp("ssh-add","ssh-add",NULL); + } + } + + return ok; // this is kind of pointless but w/e +} +enum res kill_window(const char* id) { + int fd = shm_open(id, O_RDWR, 0600); + if (fd == -1) return fail_nop; + + struct signal* s = mmap(0, sizeof(struct signal), + PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if(s == MAP_FAILED) return fail_mem; + + s -> op = mode_kill; + kill(s -> pid, SIGUSR1); + + return ok; +} +enum res activate_window(Window w) { + Display* dpy = XOpenDisplay(NULL); + XEvent ev; + long mask = SubstructureRedirectMask | SubstructureNotifyMask; + ev.xclient.type = ClientMessage; + ev.xclient.serial = 0; + ev.xclient.send_event = True; + ev.xclient.message_type = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", False); + ev.xclient.display = dpy; + ev.xclient.window = w; + ev.xclient.format = 32; + ev.xclient.data.l[0] = 2; + ev.xclient.data.l[1] = CurrentTime; + ev.xclient.data.l[2] = 0; + + if(!XSendEvent(dpy, DefaultRootWindow(dpy), False, mask, &ev)) return fail_x11; + + XSync(dpy,false); + return ok; +} + +int main(int sz, char** argv) { + enum mode op = mode_go; + bool init_weak = false; + + for(int i = 1; iwid)); + } + } else if (op == mode_register) + return bad(register_window(shid,init_weak)); + else if (op == mode_kill) + return bad(kill_window(shid)); + else if (op == mode_usage) { + write(1,"\e[1musage:\e[0m ",15); + write(1, argv[0], strlen(argv[0])); + write(1, " [-aklw [arg]]\n",16); + return fail_parse; + } +} +