pinentry-dmenu

a pinentry program based on dmenu
git clone anongit@rnpnr.xyz:pinentry-dmenu.git
Log | Files | Refs | Feed | README | LICENSE

pinentry-dmenu.c (15527B)


      1 /* See LICENSE file for copyright and license details. */
      2 #include <ctype.h>
      3 #include <locale.h>
      4 #include <signal.h>
      5 #include <stdio.h>
      6 #include <stdlib.h>
      7 #include <string.h>
      8 #include <strings.h>
      9 #include <pwd.h>
     10 #include <time.h>
     11 #include <unistd.h>
     12 
     13 #include <X11/Xlib.h>
     14 #include <X11/Xatom.h>
     15 #include <X11/Xutil.h>
     16 #ifdef XINERAMA
     17 #include <X11/extensions/Xinerama.h>
     18 #endif
     19 #include <X11/Xft/Xft.h>
     20 
     21 #include "drw.h"
     22 #include "util.h"
     23 
     24 #include "pinentry/pinentry.h"
     25 #include "pinentry/memory.h"
     26 
     27 #define INTERSECT(x,y,w,h,r)  (MAX(0, MIN((x)+(w),(r).x_org+(r).width)  - MAX((x),(r).x_org)) \
     28                              * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org)))
     29 #define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad)
     30 #define MINDESCLEN 8
     31 
     32 /* enums */
     33 enum { SchemePrompt, SchemeNormal, SchemeSelect, SchemeDesc, SchemeLast }; /* color schemes */
     34 enum { WinPin, WinConfirm };
     35 enum { Nothing, Yes, No };
     36 
     37 /* FIXME: this can't currently be set */
     38 static char *embed;
     39 static int bh, mw, mh;
     40 static int sel;
     41 static int promptw, pdescw;
     42 static int lrpad; /* sum of left and right padding */
     43 static size_t cursor;
     44 static int mon = -1, screen;
     45 
     46 static char *pin;
     47 static int pin_len;
     48 static char *pin_repeat;
     49 static int pin_repeat_len;
     50 static int repeat;
     51 
     52 static Atom clip, utf8;
     53 static Display *dpy;
     54 static Window root, parentwin, win;
     55 static XIC xic;
     56 
     57 static Drw *drw;
     58 static Clr *scheme[SchemeLast];
     59 
     60 static int timed_out;
     61 static int winmode;
     62 pinentry_t pinentry_info;
     63 
     64 #include "config.h"
     65 
     66 static int
     67 drawitem(const char* text, Bool sel, int x, int y, int w)
     68 {
     69 	unsigned int i = (sel) ? SchemeSelect : SchemeNormal;
     70 
     71 	drw_setscheme(drw, scheme[i]);
     72 
     73 	return drw_text(drw, x, y, w, bh, lrpad / 2, text, 0);
     74 }
     75 
     76 static void
     77 grabfocus(void)
     78 {
     79 	struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000  };
     80 	Window focuswin;
     81 	int i, revertwin;
     82 
     83 	for (i = 0; i < 100; ++i) {
     84 		XGetInputFocus(dpy, &focuswin, &revertwin);
     85 		if (focuswin == win)
     86 			return;
     87 		XSetInputFocus(dpy, win, RevertToParent, CurrentTime);
     88 		nanosleep(&ts, NULL);
     89 	}
     90 	die("cannot grab focus");
     91 }
     92 
     93 static void
     94 grabkeyboard(void)
     95 {
     96 	struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000  };
     97 	int i;
     98 
     99 	if (embed)
    100 		return;
    101 	/* try to grab keyboard, we may have to wait for another process to ungrab */
    102 	for (i = 0; i < 1000; i++) {
    103 		if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync,
    104 		                  GrabModeAsync, CurrentTime) == GrabSuccess)
    105 			return;
    106 		nanosleep(&ts, NULL);
    107 	}
    108 	die("cannot grab keyboard");
    109 }
    110 
    111 static size_t
    112 nextrune(int inc)
    113 {
    114 	ssize_t n;
    115 
    116 	/* return location of next utf8 rune in the given direction (+1 or -1) */
    117 	for (n = cursor + inc; n + inc >= 0 && (pin[n] & 0xc0) == 0x80; n += inc)
    118 		;
    119 	return n;
    120 }
    121 
    122 static void
    123 setup_pin(char *pin_ptr, int len, int reset)
    124 {
    125 	pin = pin_ptr;
    126 	pin_len = len;
    127 
    128 	if (reset) {
    129 		promptw = (prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
    130 		cursor = 0;
    131 
    132 		if (pin)
    133 			pin[0] = '\0';
    134 	}
    135 }
    136 
    137 static void
    138 insert(const char *str, ssize_t n)
    139 {
    140 	size_t len = strlen(pin);
    141 
    142 	// FIXME: Pinentry crashes when increasing the pin buffer the second time.
    143 	//        Other pinentry programs has a limited passwort field length.
    144 	if (len + n > pin_len - 1) {
    145 		if (repeat) {
    146 			pin_repeat_len = 2 * pin_repeat_len;
    147 			pin_repeat = secmem_realloc(pin_repeat, pin_repeat_len);
    148 			setup_pin(pin_repeat, pin_repeat_len, 0);
    149 			if (!pin_repeat)
    150 				pin_len = 0;
    151 		} else {
    152 			if (!pinentry_setbufferlen(pinentry_info, 2 * pinentry_info->pin_len))
    153 				pin_len = 0;
    154 			else
    155 				setup_pin(pinentry_info->pin, pinentry_info->pin_len, 0);
    156 		}
    157 		if (pin_len == 0) {
    158 			printf("Error: Couldn't allocate secure memory\n");
    159 			return;
    160 		}
    161 	}
    162 
    163 	/* move existing text out of the way, insert new text, and update cursor */
    164 	memmove(&pin[cursor + n], &pin[cursor], pin_len - cursor - MAX(n, 0));
    165 
    166 	if (n > 0)
    167 		memcpy(&pin[cursor], str, n);
    168 
    169 	cursor += n;
    170 	pin[len + n] = '\0';
    171 }
    172 
    173 static void
    174 drawmenu(void)
    175 {
    176 	unsigned int curpos;
    177 	int x = 0, fh = drw->fonts->h, pb, pbw = 0, i;
    178 	size_t asterlen = strlen(asterisk);
    179 	size_t pdesclen;
    180 	int leftinput;
    181 	char *censort;
    182 
    183 	char *pprompt = (repeat) ? pinentry_info->repeat_passphrase : pinentry_info->prompt;
    184 	int ppromptw = (pprompt) ? TEXTW(pprompt) : 0;
    185 
    186 	unsigned int censortl = minpwlen * TEXTW(asterisk) / strlen(asterisk);
    187 	unsigned int confirml = TEXTW(" YesNo ") + 3 * lrpad;
    188 
    189 	drw_setscheme(drw, scheme[SchemeNormal]);
    190 	drw_rect(drw, 0, 0, mw, mh, 1, 1);
    191 
    192 	if (prompt) {
    193 		drw_setscheme(drw, scheme[SchemePrompt]);
    194 		x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0);
    195 	}
    196 
    197 	if (pprompt) {
    198 		drw_setscheme(drw, scheme[SchemePrompt]);
    199 		drw_text(drw, x, 0, ppromptw, bh, lrpad / 2, pprompt, 0);
    200 		x += ppromptw;
    201 	}
    202 
    203 	if (pinentry_info->description) {
    204 		pb = mw - x;
    205 		pdesclen = strlen(pinentry_info->description);
    206 
    207 		if (pb > 0) {
    208 			pb -= (winmode == WinPin) ? censortl : confirml;
    209 			pbw = MINDESCLEN * pdescw / pdesclen;
    210 			pbw = MIN(pbw, pdescw);
    211 
    212 			if (pb >= pbw) {
    213 				pbw = MAX(pbw, pdescw);
    214 				pbw = MIN(pbw, pb);
    215 				pb = mw - pbw;
    216 
    217 				for (i = 0; i < pdesclen; i++)
    218 					if (pinentry_info->description[i] == '\n')
    219 						pinentry_info->description[i] = ' ';
    220 
    221 				drw_setscheme(drw, scheme[SchemeDesc]);
    222 				drw_text(drw, pb, 0, pbw, bh, lrpad / 2, pinentry_info->description,
    223 				         0);
    224 			} else {
    225 				pbw = 0;
    226 			}
    227 		}
    228 	}
    229 
    230 	/* Draw input field */
    231 	drw_setscheme(drw, scheme[SchemeNormal]);
    232 
    233 	if (winmode == WinPin) {
    234 		censort = ecalloc(1, asterlen * pin_len);
    235 
    236 		for (i = 0; i < asterlen * strlen(pin); i += asterlen)
    237 			memcpy(&censort[i], asterisk, asterlen);
    238 
    239 		censort[i+1] = '\n';
    240 		leftinput = mw - x - pbw;
    241 		drw_text(drw, x, 0, leftinput, bh, lrpad / 2, censort, 0);
    242 		curpos = TEXTW(censort) - TEXTW(&censort[cursor]);
    243 
    244 		if ((curpos += lrpad / 2 - 1) < leftinput) {
    245 			drw_setscheme(drw, scheme[SchemeNormal]);
    246 			drw_rect(drw, x + curpos, 2 + (bh - fh) / 2, 2, fh - 4, 1, 0);
    247 		}
    248 
    249 		free(censort);
    250 	} else {
    251 		x += TEXTW(" ");
    252 		x = drawitem("No", (sel == No), x, 0, TEXTW("No"));
    253 		x = drawitem("Yes", (sel == Yes), x, 0, TEXTW("Yes"));
    254 	}
    255 	drw_map(drw, win, 0, 0, mw, mh);
    256 }
    257 
    258 static void
    259 setup(void)
    260 {
    261 	int x, y, i, j;
    262 	unsigned int du;
    263 	XSetWindowAttributes swa;
    264 	XIM xim;
    265 	Window w, dw, *dws;
    266 	XWindowAttributes wa;
    267 	XClassHint ch = {"pinentry-dmenu", "pinentry-dmenu"};
    268 #ifdef XINERAMA
    269 	XineramaScreenInfo *info;
    270 	Window pw;
    271 	int a, di, n, area = 0;
    272 #endif
    273 	/* init appearance */
    274 	for (j = 0; j < SchemeLast; j++)
    275 		scheme[j] = drw_scm_create(drw, colors[j], 2);
    276 
    277 	clip = XInternAtom(dpy, "CLIPBOARD",   False);
    278 	utf8 = XInternAtom(dpy, "UTF8_STRING", False);
    279 
    280 	/* Calculate menu geometry */
    281 	bh = drw->fonts->h + 2;
    282 	mh = bh;
    283 #ifdef XINERAMA
    284 	i = 0;
    285 	if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) {
    286 		XGetInputFocus(dpy, &w, &di);
    287 		if (mon >= 0 && mon < n)
    288 			i = mon;
    289 		else if (w != root && w != PointerRoot && w != None) {
    290 			/* find top-level window containing current input focus */
    291 			do {
    292 				if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws)
    293 					XFree(dws);
    294 			} while (w != root && w != pw);
    295 			/* find xinerama screen with which the window intersects most */
    296 			if (XGetWindowAttributes(dpy, pw, &wa))
    297 				for (j = 0; j < n; j++)
    298 					if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) {
    299 						area = a;
    300 						i = j;
    301 					}
    302 		}
    303 		/* no focused window is on screen, so use pointer location instead */
    304 		if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du))
    305 			for (i = 0; i < n; i++)
    306 				if (INTERSECT(x, y, 1, 1, info[i]) != 0)
    307 					break;
    308 
    309 		x = info[i].x_org;
    310 		y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
    311 		mw = info[i].width;
    312 		XFree(info);
    313 	} else
    314 #endif
    315 	{
    316 		if (!XGetWindowAttributes(dpy, parentwin, &wa))
    317 			die("could not get embedding window attributes: 0x%lx",
    318 			    parentwin);
    319 		x = 0;
    320 		y = topbar ? 0 : wa.height - mh;
    321 		mw = wa.width;
    322 	}
    323 
    324 	pdescw = (pinentry_info->description) ? TEXTW(pinentry_info->description) : 0;
    325 
    326 	/* Create menu window */
    327 	swa.override_redirect = True;
    328 	swa.background_pixel = scheme[SchemePrompt][ColBg].pixel;
    329 	swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
    330 	win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0,
    331 	                    CopyFromParent, CopyFromParent, CopyFromParent,
    332 	                    CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
    333 	XSetClassHint(dpy, win, &ch);
    334 
    335 	/* input methods */
    336 	if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
    337 		die("XOpenIM failed: could not open input device");
    338 
    339 	xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
    340 	                XNClientWindow, win, XNFocusWindow, win, NULL);
    341 	XMapRaised(dpy, win);
    342 	if (embed) {
    343 		XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
    344 
    345 		if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
    346 			for (i = 0; i < du && dws[i] != win; ++i)
    347 				XSelectInput(dpy, dws[i], FocusChangeMask);
    348 			XFree(dws);
    349 		}
    350 		grabfocus();
    351 	}
    352 	drw_resize(drw, mw, mh);
    353 }
    354 
    355 static void
    356 cleanup(void)
    357 {
    358 	size_t i;
    359 
    360 	XUngrabKey(dpy, AnyKey, AnyModifier, root);
    361 	for (i = 0; i < SchemeLast; i++)
    362 		free(scheme[i]);
    363 	drw_free(drw);
    364 	XSync(dpy, False);
    365 	XCloseDisplay(dpy);
    366 }
    367 
    368 static int
    369 keypress_confirm(XKeyEvent *ev, KeySym ksym)
    370 {
    371 	if (ev->state & ControlMask) {
    372 		switch(ksym) {
    373 		case XK_c:
    374 			pinentry_info->canceled = 1;
    375 			sel = No;
    376 			return 1;
    377 		default:
    378 			return 1;
    379 		}
    380 	}
    381 
    382 	switch(ksym) {
    383 	case XK_KP_Enter:
    384 	case XK_Return:
    385 		if (sel != Nothing)
    386 			return 1;
    387 		break;
    388 	case XK_y:
    389 	case XK_Y:
    390 		sel = Yes;
    391 		return 1;
    392 	case XK_n:
    393 	case XK_N:
    394 		sel = No;
    395 		return 1;
    396 	case XK_g:
    397 	case XK_G:
    398 	case XK_Escape:
    399 		pinentry_info->canceled = 1;
    400 		sel = No;
    401 		return 1;
    402 	case XK_h:
    403 	case XK_j:
    404 	case XK_Home:
    405 	case XK_KP_Home:
    406 	case XK_Left:
    407 	case XK_KP_Left:
    408 	case XK_Prior:
    409 	case XK_KP_Prior:
    410 	case XK_Up:
    411 	case XK_KP_Up:
    412 		sel = No;
    413 		break;
    414 	case XK_k:
    415 	case XK_l:
    416 	case XK_Down:
    417 	case XK_KP_Down:
    418 	case XK_End:
    419 	case XK_Next:
    420 	case XK_Right:
    421 	case XK_KP_Right:
    422 		sel = Yes;
    423 		break;
    424 	}
    425 
    426 	return 0;
    427 }
    428 
    429 static int
    430 keypress_pin(XKeyEvent *ev, KeySym ksym, char* buf, int len)
    431 {
    432 	int old;
    433 
    434 	if (ev->state & ControlMask) {
    435 		switch(ksym) {
    436 		case XK_a: ksym = XK_Home;      break;
    437 		case XK_b: ksym = XK_Left;      break;
    438 		case XK_c: ksym = XK_Escape;    break;
    439 		case XK_d: ksym = XK_Delete;    break;
    440 		case XK_e: ksym = XK_End;       break;
    441 		case XK_f: ksym = XK_Right;     break;
    442 		case XK_g: ksym = XK_Escape;    break;
    443 		case XK_h: ksym = XK_BackSpace; break;
    444 		case XK_k:
    445 			old = cursor;
    446 			cursor = strlen(pin);
    447 			insert(NULL, old - cursor);
    448 			break;
    449 		case XK_u:
    450 			insert(NULL, -cursor);
    451 			break;
    452 		case XK_v:
    453 			XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY,
    454 			                  utf8, utf8, win, CurrentTime);
    455 			return 0;
    456 		case XK_Return:
    457 		case XK_KP_Enter:
    458 			break;
    459 		case XK_bracketleft:
    460 			pinentry_info->canceled = 1;
    461 			return 1;
    462 		default:
    463 			return 1;
    464 		}
    465 	}
    466 
    467 	switch(ksym) {
    468 	case XK_Delete:
    469 	case XK_KP_Delete:
    470 		if (pin[cursor] == '\0')
    471 			return 0;
    472 		cursor = nextrune(+1);
    473 		/* Fallthrough */
    474 	case XK_BackSpace:
    475 		if (cursor == 0)
    476 			return 0;
    477 		insert(NULL, nextrune(-1) - cursor);
    478 		break;
    479 	case XK_Escape:
    480 		pinentry_info->canceled = 1;
    481 		return 1;
    482 	case XK_Left:
    483 	case XK_KP_Left:
    484 		if (cursor > 0)
    485 			cursor = nextrune(-1);
    486 		break;
    487 	case XK_Right:
    488 	case XK_KP_Right:
    489 		if (pin[cursor] != '\0')
    490 			cursor = nextrune(+1);
    491 		break;
    492 	case XK_Home:
    493 	case XK_KP_Home:
    494 		cursor = 0;
    495 		break;
    496 	case XK_End:
    497 	case XK_KP_End:
    498 		cursor = strlen(pin);
    499 		break;
    500 	case XK_Return:
    501 	case XK_KP_Enter:
    502 		return 1;
    503 		break;
    504 	default:
    505 		if (!iscntrl((unsigned char)*buf))
    506 			insert(buf, len);
    507 	}
    508 
    509 	return 0;
    510 }
    511 
    512 static int
    513 keypress(XKeyEvent *ev)
    514 {
    515 	char buf[32];
    516 	int len;
    517 	int ret = 1;
    518 
    519 	KeySym ksym = NoSymbol;
    520 	Status status;
    521 	len = XmbLookupString(xic, ev, buf, sizeof(buf), &ksym, &status);
    522 
    523 	if (status != XBufferOverflow) {
    524 		if (winmode == WinConfirm)
    525 			ret = keypress_confirm(ev, ksym);
    526 		else
    527 			ret = keypress_pin(ev, ksym, buf, len);
    528 
    529 		if (ret == 0)
    530 			drawmenu();
    531 	}
    532 
    533 	return ret;
    534 }
    535 
    536 static void
    537 paste(void)
    538 {
    539 	char *p, *q;
    540 	int di;
    541 	unsigned long dl;
    542 	Atom da;
    543 
    544 	/* we have been given the current selection, now insert it into input */
    545 	if (XGetWindowProperty(dpy, win, utf8, 0, (pin_len/ 4) + 1, False,
    546 	                   utf8, &da, &di, &dl, &dl, (unsigned char **)&p)
    547 	    == Success && p) {
    548 		insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p));
    549 		XFree(p);
    550 	}
    551 	drawmenu();
    552 }
    553 
    554 void
    555 run(void)
    556 {
    557 	XEvent ev;
    558 
    559 	drawmenu();
    560 
    561 	while (!XNextEvent(dpy, &ev)) {
    562 		if (XFilterEvent(&ev, win))
    563 			continue;
    564 		switch(ev.type) {
    565 		case DestroyNotify:
    566 			if (ev.xdestroywindow.window != win)
    567 				break;
    568 			cleanup();
    569 			exit(1);
    570 		case Expose:
    571 			if (ev.xexpose.count == 0)
    572 				drw_map(drw, win, 0, 0, mw, mh);
    573 			break;
    574 		case KeyPress:
    575 			if (keypress(&ev.xkey))
    576 				return;
    577 			break;
    578 		case SelectionNotify:
    579 			if (ev.xselection.property == utf8)
    580 				paste();
    581 			break;
    582 		case VisibilityNotify:
    583 			if (ev.xvisibility.state != VisibilityUnobscured)
    584 				XRaiseWindow(dpy, win);
    585 			break;
    586 		}
    587 	}
    588 }
    589 
    590 /* FIXME: this does nothing */
    591 static void
    592 catchsig(int sig)
    593 {
    594 	if (sig == SIGALRM)
    595 		timed_out = 1;
    596 }
    597 
    598 static void
    599 password(void)
    600 {
    601 	winmode = WinPin;
    602 	repeat = 0;
    603 	setup_pin(pinentry_info->pin, pinentry_info->pin_len, 1);
    604 	run();
    605 
    606 	if (!pinentry_info->canceled && pinentry_info->repeat_passphrase) {
    607 		repeat = 1;
    608 		pin_repeat_len = pinentry_info->pin_len;
    609 		pin_repeat = secmem_malloc(pinentry_info->pin_len);
    610 		setup_pin(pin_repeat, pin_repeat_len, 1);
    611 		run();
    612 
    613 		pinentry_info->repeat_okay = (strcmp(pinentry_info->pin, pin_repeat) == 0)? 1 : 0;
    614 		secmem_free(pin_repeat);
    615 
    616 		if (!pinentry_info->repeat_okay) {
    617 			pinentry_info->result = -1;
    618 			return;
    619 		}
    620 	}
    621 
    622 	if (pinentry_info->canceled) {
    623 		pinentry_info->result = -1;
    624 		return;
    625 	}
    626 
    627 	pinentry_info->result = strlen(pinentry_info->pin);
    628 }
    629 
    630 static void
    631 confirm(void)
    632 {
    633 	winmode = WinConfirm;
    634 	sel = Nothing;
    635 	run();
    636 	pinentry_info->result = sel != No;
    637 }
    638 
    639 static int
    640 cmdhandler(pinentry_t received_pinentry)
    641 {
    642 	struct sigaction sa;
    643 	XWindowAttributes wa;
    644 
    645 	pinentry_info = received_pinentry;
    646 
    647 	if (!setlocale(LC_CTYPE, "") || !XSupportsLocale())
    648 		fputs("warning: no locale support\n", stderr);
    649 	if (!(dpy = XOpenDisplay(pinentry_info->display)))
    650 		die("cannot open display");
    651 	screen = DefaultScreen(dpy);
    652 	root = RootWindow(dpy, screen);
    653 	embed = (pinentry_info->parent_wid) ? embed : 0;
    654 	parentwin = (embed) ? pinentry_info->parent_wid : root;
    655 	if (!XGetWindowAttributes(dpy, parentwin, &wa))
    656 		die("could not get embedding window attributes: 0x%lx", parentwin);
    657 	drw = drw_create(dpy, screen, root, wa.width, wa.height);
    658 	if (!drw_fontset_create(drw, fonts, LENGTH(fonts)))
    659 		die("no fonts could be loaded.");
    660 	lrpad = drw->fonts->h;
    661 
    662 #ifdef __OpenBSD__
    663 	if (pledge("stdio rpath", NULL) == -1)
    664 		die("pledge");
    665 #endif
    666 
    667 	drw_setscheme(drw, scheme[SchemePrompt]);
    668 
    669 	if (pinentry_info->timeout) {
    670 		memset(&sa, 0, sizeof(sa));
    671 		sa.sa_handler = catchsig;
    672 		sigaction(SIGALRM, &sa, NULL);
    673 		alarm(pinentry_info->timeout);
    674 	}
    675 
    676 	grabkeyboard();
    677 	setup();
    678 
    679 	if (pinentry_info->pin)
    680 		do {
    681 			password();
    682 		} while (!pinentry_info->canceled && pinentry_info->repeat_passphrase
    683 		         && !pinentry_info->repeat_okay);
    684 	else
    685 		confirm();
    686 
    687 	cleanup();
    688 
    689 	return pinentry_info->result;
    690 }
    691 
    692 pinentry_cmd_handler_t pinentry_cmd_handler = cmdhandler;
    693 
    694 int
    695 main(int argc, char *argv[])
    696 {
    697 	pinentry_init("pinentry-dmenu");
    698 	pinentry_parse_opts(argc, argv);
    699 
    700 	if (pinentry_loop())
    701 		return 1;
    702 
    703 	return 0;
    704 }