summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile33
-rw-r--r--src/dynmenu.cpp821
-rwxr-xr-xsrc/samizdat-gpg-agent31
-rwxr-xr-xsrc/samizdat-password-agent73
-rw-r--r--src/samizdat-pinentry.c455
-rw-r--r--src/wait_for_files.c114
6 files changed, 1518 insertions, 9 deletions
diff --git a/Makefile b/Makefile
index 893216b..101e521 100644
--- a/Makefile
+++ b/Makefile
@@ -1,24 +1,39 @@
1prefix?=/usr/local 1prefix?=/usr/local
2 2
3all: samizdat-paths.sh 3cc_files=wait_for_files samizdat-pinentry
4cpp_files=dynmenu
4 5
5bin_programs=$(addprefix src/, xorriso-usb.sh btrfs-functions.sh btrfs-receive-root.sh btrfs-send-root.sh var.sh grub-efi.sh keygen.sh initrd.sh qemu.sh btarfs dnsmasq-dhcp-script.sh) samizdat-paths.sh 6compiled_programs=${cc_files} ${cpp_files}
6 7
7# TODO: compile these here 8CC=gcc -std=gnu99
8samizdat_execs=$(addprefix /home/d/src/samizdat/, wait_for_files samizdat-pinentry dynmenu src/samizdat-password-agent src/samizdat-gpg-agent) 9CFLAGS=-Os
9 10
10initrd_files:=$(wildcard src/initrd/*) 11all: samizdat-paths.sh ${compiled_programs}
12
13dynmenu: src/dynmenu.cpp
14 $(CXX) $(CFLAGS) $< -o $@ -lcurses
15samizdat-pinentry: src/samizdat-pinentry.c
16 $(CC) $(CFLAGS) $< -o $@ -lcurses
17wait_for_files: src/wait_for_files.c
18 $(CC) $(CFLAGS) $< -o $@
19
20bin_programs=$(addprefix src/, xorriso-usb.sh btrfs-functions.sh btrfs-receive-root.sh btrfs-send-root.sh var.sh grub-efi.sh keygen.sh initrd.sh qemu.sh btarfs dnsmasq-dhcp-script.sh samizdat-password-agent samizdat-gpg-agent) samizdat-paths.sh ${cc_files}
21
22initrd_files:=$(wildcard src/initrd/*) dynmenu samizdat-pinentry wait_for_files
11initramfs_conf_files:=$(wildcard initramfs-tools/*) 23initramfs_conf_files:=$(wildcard initramfs-tools/*)
12 24
13isolinux_files:=$(wildcard isolinux/*) 25isolinux_files:=$(wildcard isolinux/*)
14 26
15.PHONY: samizdat-paths.sh 27clean:
28 rm -f samizdat-paths.sh ${compiled_programs}
29
30.PHONY: samizdat-paths.sh all clean
16samizdat-paths.sh: src/samizdat-paths.in 31samizdat-paths.sh: src/samizdat-paths.in
17 sed -e "s?PREFIX?$(prefix)?g" $< > $@ 32 @sed -e "s?PREFIX?$(prefix)?g" $< > $@
18include samizdat-paths.sh 33include samizdat-paths.sh
19 34
20install: 35install: ${bin_programs} samizdat-paths.sh ${compiled_programs}
21 install ${bin_programs} ${samizdat_execs} ${instdir}${samizdat_bindir} 36 install ${bin_programs} ${instdir}${samizdat_bindir}
22 mkdir -p ${instdir}${samizdat_initrd_files_dir} 37 mkdir -p ${instdir}${samizdat_initrd_files_dir}
23 mkdir -p ${instdir}${samizdat_child_dir} 38 mkdir -p ${instdir}${samizdat_child_dir}
24 mkdir -p ${instdir}${samizdat_grub_efi_dir} 39 mkdir -p ${instdir}${samizdat_grub_efi_dir}
diff --git a/src/dynmenu.cpp b/src/dynmenu.cpp
new file mode 100644
index 0000000..d231639
--- /dev/null
+++ b/src/dynmenu.cpp
@@ -0,0 +1,821 @@
1#include <unistd.h>
2#include <curses.h>
3#include <string.h>
4#include <string>
5#include <vector>
6#include <assert.h>
7#include <algorithm>
8#include <signal.h>
9
10class DynmenuView;
11typedef DynmenuView *DynmenuViewRef; // boost::shared_ptr<DynmenuView>
12
13class Dynmenu
14{
15 std::string m_title;
16 std::string m_welcomeText;
17 std::vector<DynmenuViewRef> views;
18
19 class Item {
20 char *desc;
21 Item(const Item &i) { assert(false); }
22 public:
23 std::string id;
24 std::string key;
25 std::string cmd;
26 const char *getText() const { return desc; }
27 void setText(const char *desc) {
28 if(this->desc) delete [] this->desc;
29 this->desc=new char[strlen(desc)+1];
30 strcpy(this->desc,desc);
31 }
32 Item(const char *id, const char *key, const char *desc, const char *cmd) :
33 id(id), key(key), cmd(cmd) { this->desc=NULL; setText(desc); }
34 ~Item() { delete [] desc; }
35 };
36
37 std::vector<Item *> items;
38
39 typedef std::vector<Item *>::iterator ItemIterator;
40 struct has_id : public std::binary_function<Item*,const char *,bool> {
41 bool operator ()( Item *item , const char *id) const { return item->id==id; }
42 };
43
44 int m_selection;
45
46public:
47
48 enum notify_id { TITLE, WELCOMETEXT, SELECTION };
49 void redraw() { sendNotify(TITLE); sendNotify(WELCOMETEXT); sendNotify(SELECTION); }
50 void setTitle(const char *s) { m_title = s; sendNotify(TITLE); }
51 const char*title() const { return m_title.c_str(); }
52 void setWelcomeText(const char *s) { m_welcomeText = s; sendNotify(WELCOMETEXT); }
53 const char *welcomeText() const { return m_welcomeText.c_str(); }
54 void setItem(const char *id, const char *sortkey, const char *desc, const char *cmd);
55 void delItem(const char *id);
56 int selection() const { return m_selection; }
57 bool haveSelection() const { return m_selection >= 0; }
58 void selectItem(const char *id);
59 const char *selectedCommand() const { return haveSelection() ? items[m_selection]->cmd.c_str() : ""; }
60 void cursorUp() { if( m_selection>0) m_selection--; sendNotify(SELECTION); }
61 void cursorDown() { if( m_selection+1<items.size()) m_selection++; sendNotify(SELECTION); }
62 Dynmenu() { m_selection = -1; }
63 ~Dynmenu() { for(int i=0;i<items.size();i++) delete items[i]; }
64
65 void registerView(DynmenuViewRef view);
66private:
67 void sendNotify(notify_id id);
68 void sendItemInsertion(int idx, const char *text);
69 void sendItemChanged(int idx, const char *text);
70 ItemIterator findItem(const char *id);
71};
72
73class DynmenuView
74{
75public:
76 virtual void notify(Dynmenu *menu, Dynmenu::notify_id id) = 0;
77 virtual void itemInserted(Dynmenu *menu, int index, const char *text ) = 0;
78 virtual void itemChanged(Dynmenu *menu, int index, const char *text ) = 0;
79 virtual ~DynmenuView() {}
80};
81
82class DynmenuSimpleView : public DynmenuView
83{
84public:
85 virtual void notify(Dynmenu *menu, Dynmenu::notify_id id) {
86 printf("notify %p %i\n", menu, id );
87 }
88 virtual void itemInserted(Dynmenu *menu, int index, const char *text ) {
89 //printf("itemInserted %p %i \"%s\" \"%s\"\n", menu, index, text, menu->items[index]->cmd.c_str() );
90 printf("itemInserted %p %i \"%s\"\n", menu, index, text );
91 }
92 virtual void itemChanged(Dynmenu *menu, int index, const char *text ) {
93 printf("itemChanged %p %i \"%s\"\n", menu, index, text );
94 }
95 virtual ~DynmenuSimpleView() {}
96};
97
98
99Dynmenu::ItemIterator Dynmenu::findItem(const char *id)
100{
101 using namespace std;
102 ItemIterator ret = find_if( items.begin(), items.end(), bind2nd( has_id(), id ) );
103 return ret;
104}
105
106void Dynmenu::setItem(const char *id, const char *sortkey, const char *desc, const char *cmd)
107{
108 std::vector<Item*>::iterator i;
109 i = findItem(id);
110 if(i==items.end()) {
111 items.push_back( new Item(id,sortkey,desc,cmd) );
112 // TODO: sort
113 sendItemInsertion( items.size()-1, items.back()->getText() );
114 if(m_selection==-1) {
115 m_selection=items.size()-1;
116 sendNotify(SELECTION);
117 }
118 }
119 else {
120 (*i)->cmd = cmd;
121 (*i)->setText(desc);
122 (*i)->key = sortkey; // TODO: resort
123 sendItemChanged( i - items.begin(), (*i)->getText() );
124 }
125}
126
127void Dynmenu::selectItem(const char *id)
128{
129 std::vector<Item*>::iterator i;
130 i = findItem(id);
131 if(i!=items.end()) {
132 m_selection = i - items.begin();
133 sendNotify(SELECTION);
134 }
135}
136
137void Dynmenu::delItem(const char *id){}
138
139void Dynmenu::registerView(DynmenuViewRef view)
140{
141 view->notify(this,TITLE);
142 view->notify(this,WELCOMETEXT);
143 std::vector<Item*>::iterator i;
144 for(i=items.begin(); i!=items.end(); i++ ) {
145 view->itemInserted( this, i - items.begin(), (*i)->getText() );
146 }
147 view->notify(this,SELECTION);
148 views.push_back(view);
149}
150
151void Dynmenu::sendNotify(notify_id id)
152{
153 for( std::vector<DynmenuViewRef>::iterator i=views.begin(); i!=views.end(); i++ ) {
154 (*i)->notify(this, id);
155 }
156}
157void Dynmenu::sendItemInsertion(int idx, const char *text )
158{
159 for( std::vector<DynmenuViewRef>::iterator i=views.begin(); i!=views.end(); i++ ) {
160 (*i)->itemInserted(this, idx, text);
161 }
162}
163void Dynmenu::sendItemChanged(int idx, const char *text )
164{
165 for( std::vector<DynmenuViewRef>::iterator i=views.begin(); i!=views.end(); i++ ) {
166 (*i)->itemChanged(this, idx, text);
167 }
168}
169
170int _g_y = 0;
171
172#define kdprintf(fmt,...) \
173 { char buf[200]; _g_y++; sprintf( buf, fmt "\n", ## __VA_ARGS__); mvaddnstr(_g_y,1,buf,strlen(buf)); refresh(); }
174#define dprintf(fmt,...)
175
176
177struct TextBox
178{
179 int y,x,w,h;
180 TextBox() : y(0),x(0),w(0),h(0) {}
181 void update(int x_, int y_, int w_, int h_) { y=y_;x=x_;w=w_;h=h_; }
182};
183
184
185class LineDrawer
186{
187public:
188 virtual void drawLine(WINDOW *win, int width, int number) = 0;
189 virtual int size() const = 0;
190};
191
192class ScrollWindow
193{
194 WINDOW *win;
195 int top_index;
196 LineDrawer *drawer;
197 void getExtants(int &maxy, int &maxx, int &count);
198public:
199 void redraw();
200 ScrollWindow() : win(NULL), top_index(0), drawer(NULL) {}
201 void setWin(WINDOW *w) { win=w; redraw(); }
202 void setLineDrawer(LineDrawer *d) { drawer = d; redraw(); }
203 void doScroll(int delta);
204 void scrollTo(int start, int end);
205 void invalidate(int start, int end);
206 void changedSize();
207};
208
209void ScrollWindow::getExtants(int &maxy, int &maxx, int &count)
210{
211 getmaxyx(win,maxy,maxx);
212 count = drawer->size();
213}
214
215void ScrollWindow::redraw()
216{
217 if(!win || !drawer) return;
218 int maxy,maxx,count;
219 getExtants(maxy,maxx,count);
220 wborder(win, 0, 0, top_index>0 ? '.' : 0, count < top_index+maxy-1 ? 0 : '.' , 0, 0, 0, 0);
221 for(int y=1,i=top_index;y<maxy-1;y++) {
222 if( i<count ) {
223 wmove(win,y,1);
224 drawer->drawLine(win,maxx-2,i++);
225 }
226 }
227 wrefresh(win);
228}
229
230void ScrollWindow::invalidate(int start, int end)
231{
232 int maxy,maxx,count;
233 if( end <= top_index ) return;
234 getExtants(maxy,maxx,count);
235 if( start > top_index+maxy-2 ) return;
236
237 if(start < top_index ) start = top_index;
238 if(end > top_index+maxy-2 ) end = top_index+maxy-2;
239 //if(end > count ) end = count;
240
241
242 int starty = 1 + start - top_index;
243 int stopy = starty + end - start;
244
245 for(int y=starty,i=start;y<stopy;y++) {
246 //if( i<count ) {
247 wmove(win,y,1);
248 drawer->drawLine(win,maxx-2,i++);
249 //}
250 }
251 wrefresh(win);
252}
253
254void ScrollWindow::scrollTo(int start, int end)
255{
256 if( start < top_index ) doScroll(start-top_index);
257 int maxy,maxx,count;
258 getExtants(maxy,maxx,count);
259 if( end > top_index+maxy-2 && start>top_index) {
260 int delta=end-top_index-maxy+2;
261 if( top_index+delta > start ) delta = start - top_index;
262 doScroll(delta);
263 }
264}
265
266void ScrollWindow::changedSize()
267{
268 int maxy,maxx,count;
269 getExtants(maxy,maxx,count);
270 wmove(win,maxy-1,1);
271 if( top_index+maxy-2<count) {
272 whline(win,'.',maxx-2);
273 wrefresh(win);
274 }
275 else if( top_index+maxy-2==count) {
276 whline(win,0,maxx-2);
277 wrefresh(win);
278 }
279}
280
281void ScrollWindow::doScroll(int delta)
282{
283 if(!win || !drawer) return;
284 //assert(delta>=-1 && delta<=1);
285 int maxy,maxx,count;
286 getExtants(maxy,maxx,count);
287
288 if( top_index+delta<0 ) delta = -top_index;
289 if (delta>0 && top_index>0 && top_index+maxy-1+delta>count) delta=count-top_index-maxy+2;
290 if (top_index+delta>count) delta=0;
291
292 if(delta==0) return;
293 dprintf("scrolling win=%p, drawer=%p, top_index+delta=%i, v=%i, count=%i", win, drawer, top_index+delta, top_index+maxy-1+delta, count);
294
295 dprintf("scrolling %i", delta);
296
297 WINDOW *w = derwin(win, maxy-2,maxx-2,1,1);
298 scrollok(w,true);
299 wscrl(w,delta);
300 scrollok(w,false);
301 touchwin(win);
302
303 if( top_index==0 ) {
304 wmove(win,0,1);
305 whline(win,'.',maxx-2);
306 }
307 if( top_index+maxy-2==count) {
308 wmove(win,maxy-1,1);
309 whline(win,'.',maxx-2);
310 }
311 top_index += delta;
312 int y = delta<0 ? 0 : maxy-2-delta;
313 int i = y + top_index;
314 dprintf("scroll values i=%i, y=%i", i, y);
315 delta = delta<0 ? -delta : delta;
316 for(int j=0;j<delta;j++) {
317 wmove(w,y++,0);
318 if(i<count) drawer->drawLine(w,maxx-2,i++);
319
320 }
321 wrefresh(w);
322 delwin(w);
323 if( top_index==0 ) {
324 wmove(win,0,1);
325 whline(win,0,maxx-2);
326 }
327 if( top_index+maxy-2==count) {
328 wmove(win,maxy-1,1);
329 whline(win,0,maxx-2);
330 }
331 wrefresh(win);
332}
333
334/*
335class ItemDrawer : public LineDrawer
336{
337public:
338 void drawLine(WINDOW *win, int width, int number);
339 int size() const { return 50; }
340};
341
342void ItemDrawer::drawLine(WINDOW *win, int width, int number)
343{
344 char buf[200];
345 sprintf(buf, "This is line #%i.", number );
346 waddnstr(win,buf, width);
347}
348*/
349
350class DynmenuCursesView : public DynmenuView, public LineDrawer
351{
352 int maxx, maxy;
353 WINDOW *win;
354 TextBox titleBox;
355 TextBox welcomeBox;
356 WINDOW *itemwin;
357 ScrollWindow itemScroll;
358 struct Item {
359 int top;
360 std::vector<const char *> text;
361 int compare(int line) {
362 if(line<top) return -1;
363 else if(line>=top+text.size()) return 1;
364 else return 0;
365 }
366 };
367 std::vector<Item> items;
368 int cached_index;
369 int selected;
370public:
371 DynmenuCursesView() : win(NULL),itemwin(NULL), maxx(0), maxy(0), cached_index(0), selected(-1) {
372 itemScroll.setLineDrawer(this);
373 }
374 ~DynmenuCursesView() { delwin(itemwin); }
375 void show(WINDOW *win);
376 void notify(Dynmenu *menu, Dynmenu::notify_id id);
377 void itemInserted(Dynmenu *menu, int index, const char *text );
378 void itemChanged(Dynmenu *menu, int index, const char *text );
379
380 // LineDrawer interface
381 void drawLine(WINDOW *win, int width, int number);
382 int size() const { if(items.size()==0) return 0; else return items.back().top + items.back().text.size(); }
383};
384
385void DynmenuCursesView::drawLine(WINDOW *win, int width, int number )
386{
387 int i = cached_index;
388 while( items[i].compare(number)>0 && i<items.size()-1 ) i++;
389 while( items[i].compare(number)<0 && i>0 ) i--;
390 if( items[i].compare(number)!=0 ) {
391 // If out of range, clear row
392 wattroff(win, A_STANDOUT);
393 for(int i=0;i<width;i++) waddch(win,' ');
394 return;
395 }
396 cached_index = i;
397 Item &item = items[cached_index];
398 i = number - item.top;
399 const char *end = NULL;
400 if( i < item.text.size()-1 ) end = item.text[i+1]-1;
401 if(!end) for(end=item.text[i];*end;end++);
402 size_t len = end - item.text[i];
403 len = len>width ? width : len;
404 // TODO: draw selection
405 if( selected == cached_index )
406 wattron(win, A_STANDOUT);
407 else
408 wattroff(win, A_STANDOUT);
409 waddnstr( win, item.text[i], len );
410 int mright = width - len;
411 for(int j=0;j<mright;j++) waddstr(win," ");
412 wattroff(win, A_STANDOUT);
413}
414
415void DynmenuCursesView::itemInserted(Dynmenu *menu, int index, const char *text)
416{
417 std::vector<Item>::iterator item = items.insert( items.begin() + index, Item() );
418 const char *p = text;
419 item->text.push_back(p);
420 for(;;) {
421 while( *p && *p!='\n' ) p++;
422 if(*p) p++;
423 if(*p) item->text.push_back(p);
424 else break;
425 }
426 int firstrow = 0;
427 if(item!=items.begin()) {
428 item->top = (item-1)->top + (item-1)->text.size();
429 }
430 else
431 item->top = 0;
432
433 firstrow = item->top;
434 if((item+1)!=items.end()) {
435 size_t newrows = item->text.size();
436 for( item++; item!=items.end(); item++ ) item->top += newrows;
437 }
438 itemScroll.invalidate( firstrow, size() );
439 itemScroll.changedSize();
440 wrefresh(win);
441}
442
443void DynmenuCursesView::itemChanged(Dynmenu *menu, int index, const char *text)
444{
445 std::vector<Item>::iterator item = items.begin() + index;
446 size_t oldsize = item->text.size();
447 int lastrow = items.back().top + items.back().text.size();
448 item->text.clear();
449 const char *p = text;
450 item->text.push_back(p);
451 for(;;) {
452 while( *p && *p!='\n' ) p++;
453 if(*p) p++;
454 if(*p) item->text.push_back(p);
455 else break;
456 }
457 int firstrow = item->top;
458 int diff = item->text.size() - oldsize;
459 if( diff ) {
460 for( item ++; item!=items.end(); item++ ) item->top += diff;
461 item = items.end() - 1;
462 }
463 else lastrow = item->top + oldsize;
464 if( lastrow < item->top + item->text.size() ) lastrow = item->top + item->text.size();
465
466 itemScroll.invalidate( firstrow, lastrow );
467 itemScroll.changedSize();
468 wrefresh(win);
469}
470
471void getTextExtents(const char *text, int &width, int &height )
472{
473 width = 0;
474 height = 0;
475 const char *p;
476 const char *s;
477 p = text;
478 do {
479 s = p;
480 while( *p && *p!='\n' ) p++;
481 if(p-s>0) {
482 height++;
483 if(width<p-s) width=p-s;
484 } else if (*p) height++;
485 if(*p) p++;
486 } while( *p );
487}
488
489void drawText( WINDOW *win, const TextBox &b, const char *text, const bool center = false )
490{
491 int cx,cy;
492 const char *p;
493 const char *s;
494 cx = b.x;
495 cy = b.y;
496 p = text;
497 do {
498 s = p;
499 while( *p && *p!='\n' ) p++;
500 int len = p-s;
501 if(len && cy<b.y+b.h ) {
502 int offset = center ? (b.w - cx - len) / 2 : 0;
503 int res = mvwaddnstr( win, cy, cx + offset, s, len);
504 }
505 cy++;
506 if(*p) p++;
507 } while( *p );
508}
509
510void clearBox( WINDOW *win, const TextBox &b )
511{
512 if(b.w==0 || b.h==0 ) return;
513 dprintf("clearing: h=%i, w=%i, y=%i, x=%i", b.h, b.w, b.y,b.x);
514 WINDOW *w = derwin(win,b.h,b.w,b.y,b.x);
515 werase(w);
516 touchwin(win);
517 wrefresh(w);
518 delwin(w);
519}
520
521void DynmenuCursesView::notify(Dynmenu *menu, Dynmenu::notify_id id)
522{
523 int tx,ty, tw,th, oldbottom;
524 switch(id) {
525 case Dynmenu::TITLE:
526 getTextExtents( menu->title(), tw, th );
527 tx = 0;
528 ty = 0;
529 tw = maxx - tx;
530 dprintf("tx = %i, ty=%i, title=%s, tw=%i", tx, ty, menu->title(), tw );
531 clearBox(win,titleBox);
532 oldbottom = titleBox.y+titleBox.h;
533 titleBox.update(tx,ty,tw,th);
534 if(oldbottom!=titleBox.y+titleBox.h) {
535 notify(menu,Dynmenu::WELCOMETEXT);
536 }
537 drawText(win,titleBox, menu->title(), true );
538 break;
539 case Dynmenu::WELCOMETEXT:
540 getTextExtents( menu->welcomeText(), tw, th );
541 tx = ( maxx - tw )/ 2;
542 ty = titleBox.y+titleBox.h+2;
543 clearBox(win,welcomeBox);
544 if(ty+th!=welcomeBox.y+welcomeBox.h) {
545 // Need to resize itemsBox
546 if(itemwin) {
547 werase(itemwin);
548 delwin(itemwin);
549 }
550 itemwin = derwin(win,maxy-ty-th-1,maxx-3, ty+th, 2);
551 itemScroll.setWin(itemwin);
552 }
553 welcomeBox.update(tx,ty,tw,th);
554 drawText(win,welcomeBox, menu->welcomeText() );
555 //mvwaddstr(win,ty,tx, menu->welcomeText() );
556 break;
557 case Dynmenu::SELECTION:
558 //dprintf("items=%zu, selection=%i", menu->items.size(), menu->selection() );
559 int sel=selected;
560 selected = menu->selection();
561 if(sel!=-1)
562 itemScroll.invalidate(items[sel].top, items[sel].top+items[sel].text.size() );
563 sel = selected;
564 if(sel!=-1) {
565 itemScroll.invalidate(items[sel].top, items[sel].top+items[sel].text.size() );
566 itemScroll.scrollTo(items[sel].top, items[sel].top+items[sel].text.size() );
567 wrefresh(itemwin);
568 }
569 break;
570 }
571 wrefresh(win);
572}
573
574void DynmenuCursesView::show(WINDOW *win)
575{
576 if( win ) {
577 this->win = win;
578 getmaxyx(win,maxy,maxx);
579 //box(win, 0, 0);
580 if(itemwin) { wrefresh(itemwin); }
581 wrefresh(win);
582 }
583 else {
584 }
585}
586
587#include <string>
588#include <cctype>
589
590using std::string;
591using std::isspace;
592
593class Parser
594{
595 int peeked;
596 void *gdata;
597 int (*getter)(void *);
598 int peek() { return peeked==-2 ? (peeked=getter(gdata)) : peeked; }
599 int get() { int retv = peeked==-2? (peeked=getter(gdata)) : peeked; peeked=-2; return retv;}
600 bool peekedSpace() { return isspace(peek()); }
601 void skipSpace() { while(peekedSpace()) get(); }
602public:
603 enum tokentype { Identifier, String, EOL };
604 enum exception { NoGetter, SyntaxError };
605 void setCharGetter( int (*g)(void*), void *user ) { getter=g; gdata=user; }
606 void expect( tokentype t, std::string &s );
607 Parser() : peeked(-2), getter(NULL), gdata(NULL) {}
608};
609
610void Parser::expect( tokentype t, std::string &s )
611{
612 if(! getter ) throw NoGetter;
613
614 int c;
615
616 switch( t ) {
617 case Identifier:
618 skipSpace();
619 s = "";
620 while( peek()!=-1 && ! peekedSpace() ) s += (char)get() ;
621 break;
622 case String:
623 skipSpace();
624 s = "";
625 if( peek() !='"' ) throw SyntaxError;
626 get(); // peel off open-quote
627 while(peek()!='"' && peek()!=-1 ) {
628 c = get();
629 if( c=='\\') {
630 c=get(); // allow escaped quotes
631 if(c=='n') c='\n';
632 else if(c=='t') c='\t';
633 }
634 s += (char)c ;
635 }
636 get(); // peel off close-quote
637 break;
638 case EOL:
639 while( peek()!=-1 && isspace(peek()) && peek()!='\n' ) get();
640 if( !isspace(peek()) ) throw SyntaxError;
641 break;
642 }
643}
644
645
646
647
648#include <sys/select.h>
649#include <errno.h>
650#include <fcntl.h>
651#include <stdio.h>
652
653#define CTRLL 12
654
655
656int buf_getc( const char **p )
657{
658 if(**p) return *((*p)++);
659 else return -1;
660}
661
662void trap_signals()
663{
664 struct sigaction s;
665 s.sa_handler = SIG_IGN;
666 int sig[] = { SIGINT, SIGTSTP, SIGQUIT, };
667 for (int i=0; i<sizeof(sig)/sizeof(sig[0]); ++i)
668 sigaction(sig[i], &s, 0);
669}
670
671void bye(void) { if (!isendwin()) endwin(); }
672
673int main(int argc, char **argv)
674{
675 // char buf1[3]; strcpy(buf1, "joehere"); printf("buf=\"%s\"\n");
676 if (argc != 2) {
677 fputs("usage: dynmenu socket-file\n", stderr);
678 return 1;
679 }
680
681 trap_signals();
682
683 Dynmenu menu;
684
685//#define SIMPLE
686#ifdef SIMPLE
687 DynmenuSimpleView view;
688#else
689 DynmenuCursesView view;
690 WINDOW *win = initscr();
691 atexit(bye);
692 curs_set(0); cbreak(); noecho(); keypad(stdscr, 1);
693 wclear(win);
694 view.show(win);
695#endif
696 menu.registerView( &view );
697 int fd;
698
699 fd_set fds;
700 int res;
701 if ((fd = open((const char *) argv[1], O_RDWR|O_NONBLOCK)) < 0) {
702 perror("error opening socket-file");
703 return 1;
704 }
705 FILE *fifo;
706 if ((fifo = fdopen(fd, "r")) == NULL) {
707 perror("error opening socket-file: fdopen");
708 return 1;
709 }
710
711 FD_ZERO(&fds); FD_SET(fd, &fds); FD_SET(0, &fds);
712top:
713 /* Note: On terminal resize, select returns -1 and sets errno to EINTR */
714 dprintf("mainloop...");
715 while ((res = select(fd+1, &fds, NULL, NULL, NULL)) >= 0)
716 {
717 if (FD_ISSET(0, &fds)) {
718 //dprintf("char !");//space!");
719 int c = getch();
720 if (c == ERR) {
721 perror("error reading stdin");
722 return 1;
723 }
724 switch (c) {
725 case KEY_UP: case 'k': menu.cursorUp(); break;
726 case KEY_DOWN: case 'j': menu.cursorDown(); break;
727 case KEY_RIGHT: case '\n': case '\x0d': case 'l':
728 case KEY_ENTER:
729 if (!menu.haveSelection())
730 break;
731 bye();
732 endwin();
733 execl("/bin/sh", "/bin/sh", "-c", menu.selectedCommand(), (char *) NULL);
734 perror("exec failed");
735 exit(1);
736 break;
737
738 case 'Q': bye(); endwin(); exit(0); break;
739
740#ifndef SIMPLE
741 case 'r':
742 case CTRLL: menu.redraw(); break;
743#endif
744/*
745 case ' ':
746 {
747 dprintf("space!");
748 static char b = 'A';
749 char buf[80]; sprintf( buf, "Letter '%c'", b++ );
750 menu.setItem( "id3", "3", buf, "id3 cmd");
751 }
752*/
753 break;
754 }
755 }
756 if (FD_ISSET(fd, &fds)) {
757 Parser *p = new Parser;
758 char line[1024];//[PIPE_BUF];
759 assert(line);
760 char *pointer = line;
761 while (fgets(line, 1024, fifo) != NULL) {
762 dprintf("%s", line );
763 try {
764 pointer = line;
765 string menuMethod(1024,' ');
766 string id(1024, ' '); string sortkey(1024, ' ');
767 string desc(1024, ' '); string cmd(1024, ' '); string dummy;
768 p->setCharGetter( (int (*)(void*))buf_getc, (void*)&pointer );
769 p->expect( Parser::Identifier, menuMethod );
770 if( menuMethod=="setItem" ) {
771 p->expect( Parser::String, id );
772 p->expect( Parser::String, sortkey );
773 p->expect( Parser::String, desc );
774 p->expect( Parser::String, cmd );
775 menu.setItem( id.c_str(), sortkey.c_str(), desc.c_str(), cmd.c_str() );
776 }
777 else if( menuMethod=="setTitle" ) {
778 p->expect( Parser::String, desc );
779 menu.setTitle( desc.c_str() );
780 }
781 else if( menuMethod=="setWelcomeText" ) {
782 p->expect( Parser::String, desc );
783 menu.setWelcomeText( desc.c_str() );
784 }
785 else if( menuMethod=="delItem" ) {
786 p->expect( Parser::String, id );
787 menu.delItem( id.c_str() );
788 }
789 else if( menuMethod=="selectItem" ) {
790 p->expect( Parser::String, id );
791 menu.selectItem( id.c_str() );
792 }
793 }
794 catch(...) {
795 dprintf("exception");
796 }
797 }
798 }
799 FD_ZERO(&fds);
800 if (!feof(fifo)) // will never eof if it is a fifo
801 FD_SET(fd, &fds); // could use inotify here...
802 FD_SET(0, &fds);
803 }
804 char ebuf[80] ="";
805 switch(errno)
806 {
807 case EINTR:
808 menu.redraw();
809 goto top;
810 case EBADF: strcpy(ebuf, "EBADF"); break;
811 case EINVAL: strcpy(ebuf, "EINVAL"); break;
812 case ENOMEM: strcpy(ebuf, "ENOMEM"); break;
813 default: sprintf(ebuf, "0x%X", errno);
814 }
815 endwin();
816 printf(" errno = %s\n", ebuf );
817
818 return 0;
819}
820
821// vim:ts=2 sw=2 et:
diff --git a/src/samizdat-gpg-agent b/src/samizdat-gpg-agent
new file mode 100755
index 0000000..5b09b7b
--- /dev/null
+++ b/src/samizdat-gpg-agent
@@ -0,0 +1,31 @@
1#!/bin/sh
2export GNUPGHOME=/gpg/gnupghome
3do_ps()
4{
5 . "$GNUPGHOME"/gpg-agent-info
6 GPG_PID=${GPG_AGENT_INFO#*:}
7 GPG_PID=${GPG_PID%:*}
8 /bin/ps uwww $GPG_PID >&2
9}
10
11sh_exports()
12{
13 cat "$GNUPGHOME"/gpg-agent-info || exit
14 echo GNUPGHOME="'$GNUPGHOME'"
15 echo export GNUPGHOME GPG_AGENT_INFO SSH_AUTH_SOCK SSH_AGENT_PID
16}
17
18gpg-agent --daemon \
19 --enable-ssh-support \
20 --allow-preset-passphrase \
21 --use-standard-socket \
22 --homedir "$GNUPGHOME" \
23 --write-env-file "$GNUPGHOME"/gpg-agent-info
24
25case $? in
26 0) ;;
27 2) [ "$verbose" ] && do_ps ;;
28 *) exit $? ;;
29esac
30
31sh_exports
diff --git a/src/samizdat-password-agent b/src/samizdat-password-agent
new file mode 100755
index 0000000..0fb26c6
--- /dev/null
+++ b/src/samizdat-password-agent
@@ -0,0 +1,73 @@
1#!/bin/sh
2[ "$(id -u)" -gt 0 ] && exec sudo "$0" "$@"
3
4scan_gnupg_db()
5{
6 keylist=
7 mainkeygrip=
8 uid=
9 local IFS='
10'
11 for record in $(gpg2 --with-fingerp --with-fingerp --with-colons -K | cut -d: -f1,5,10); do
12 : "$record"
13 case "$record" in
14 fpr:*) keylist="$keylist ${record##*:}" ;;
15 sec:*)
16 if [ -z "$mainkeygrip" ]; then
17 mainkeygrip="${record#sec:}"
18 mainkeygrip="${mainkeygrip%%:*}"
19 fi ;;
20 uid:*) : ${uid:=${record#uid:*:}} ;;
21 esac
22 done
23}
24
25eval $(samizdat-gpg-agent)
26
27gpg2_test_sign()
28{
29 GPG_TTY=none gpg2 \
30 --ignore-valid-from --ignore-time-conflict --no-tty --batch --clearsign </dev/null >/dev/null 2>&1
31}
32
33if [ ! "$FORCE_PINENTRY" ] && gpg2_test_sign; then
34 echo "Made signature with default key successfully. No additional passphrase is needed." >&2
35 exit
36fi
37
38scan_gnupg_db # get $uid
39
40if [ "$uid" ]; then
41 DESC='Please enter the passphrase to unlock the secret key for the OpenPGP certificate:%0A'"'$uid'."
42else
43 DESC='Please enter the passphrase to unlock the secret key for the OpenPGP certificate.'
44fi
45
46exec samizdat-pinentry \
47 --setdesc "$DESC" \
48 --ttyname "$(tty)" \
49 --socket "${GPG_AGENT_INFO%%:*}" \
50 --resocket /root/"${GPG_AGENT_INFO%%:*}" \
51 --tell-immediately '
52 read secret
53 for grip in '"$keylist"' -; do
54 read status || break
55 printf "< %s\n" "$status" >&2
56 [ -z "${status##OK*}" ] || break
57 [ "$grip" = - ] && break
58 printf "> PRESET_PASSPHRASE %s -1 %%s\n" "$grip" >&2
59 printf "PRESET_PASSPHRASE %s -1 %s\n" "$grip" "$secret"
60 done
61 ' \
62 --validate '
63 exec 7<&0
64 gpgoutput=$(gpg2 --batch --no-tty --ignore-valid-from --ignore-time-conflict \
65 --passphrase-fd 7 --default-key '"$mainkeygrip"' --clearsign </dev/null >/dev/null)
66 status=$?
67 case "$gpgoutput" in
68 *"Bad passphrase"*) echo "Error: Bad passphrase." ;;
69 "") [ $status -eq 0 ] || echo "Error: gpg returned $status." ;;
70 *) printf "gpg returned unexpected error (exit status $status):\n%s\n" "$gpgoutput" ;;
71 esac
72 exit $status
73 ' \
diff --git a/src/samizdat-pinentry.c b/src/samizdat-pinentry.c
new file mode 100644
index 0000000..e1f7fee
--- /dev/null
+++ b/src/samizdat-pinentry.c
@@ -0,0 +1,455 @@
1#include <error.h>
2#include <unistd.h>
3#include <stdlib.h>
4#include <stdio.h>
5#include <errno.h>
6#include <string.h>
7#include <sys/types.h>
8#include <sys/wait.h>
9#include <sys/socket.h>
10#include <sys/un.h>
11
12
13// TODO: implement multiple ttys
14// TODO: inotify on a password file (kills pinentry)
15// TODO: secure memory
16
17
18/* Buffers:
19 *
20 * assuan info page "The implementation is line based with a maximum line size
21 * of 1000 octets. The default IPC mechanism are Unix Domain Sockets.
22 *
23 * pinentry info page "Although it is called a PIN-Entry, it does allow to
24 * enter reasonably long strings (at least 2048 characters are supported by
25 * every pinentry). The client using the PIN-Entry has to check for
26 * correctness."
27 */
28#define MAXLINE 1000 // It's assumed this is > 2.
29
30
31static int
32open2(const char *argv[], int *result_in, int *result_out, pid_t *result_child)
33{
34 int pipe_out[2]; /* out of parent */
35 int pipe_in[2]; /* into parent */
36 pid_t cpid;
37
38 if (pipe(pipe_out) < 0)
39 return -1;
40 if (pipe(pipe_in) < 0)
41 return -1;
42
43 cpid = fork();
44 if (cpid < 0)
45 return -1;
46
47 if (cpid == 0) { /* child */
48 int *input = pipe_out; /* for sanity */
49 int *output = pipe_in;
50 close(output[0]);
51 close(input[1]);
52
53 /* see http://unixwiz.net/techtips/remap-pipe-fds.c.txt */
54 if (output[1] == 0)
55 if ((output[1] = dup(output[1]) < 0))
56 error(1, errno, "in child process: dup");
57 if (output[0] == 1)
58 if ((output[0] = dup(output[0]) < 0))
59 error(1, errno, "in child process: dup");
60
61 if (dup2(output[1], 1) < 0)
62 error(1, errno, "in child process: dup2");
63 if (dup2(input[0], 0) < 0)
64 error(1, errno, "in child process: dup2");
65
66 if (output[1] != 1)
67 close(output[1]);
68 if (input[0] != 0)
69 close(input[0]);
70
71 execv(argv[0],(char *const*) &argv[1]);
72 error(1, errno, "in child process: exec");
73 return -1; /* fucking warning */
74
75 } else { /* parent */
76 close(pipe_out[0]);
77 close(pipe_in[1]);
78
79 *result_in = pipe_in[0];
80 *result_out = pipe_out[1];
81 if (*result_child)
82 *result_child = cpid;
83 return 0;
84 }
85}
86
87typedef struct {
88 FILE *in, *out;
89 pid_t pid;
90} pipe2_t;
91
92
93pipe2_t fopen2(const char *argv[])
94{
95 pipe2_t res = { 0, 0, 0 };
96 int in = 0;
97 int out = 0;
98 if( open2(argv, &in, &out, &res.pid) < 0 )
99 error(1,0, "open2");
100 res.out = fdopen(out, "w");
101 res.in = fdopen(in, "r");
102 return res;
103}
104
105void pinentry_send(pipe2_t pinentry, const char *msg)
106{
107 fprintf(pinentry.out, "%s\n", msg);
108 fflush(pinentry.out);
109 // printf("C: %s\n", msg);
110}
111int pinentry_get(pipe2_t pinentry, char *data, size_t len)
112{
113 char *ret=fgets(data,len,pinentry.in);
114 char *p=ret;
115 int gotnewline=0;
116 for(;*p;p++);
117 if(p!=ret && p[-1]=='\n') {
118 p[-1]='\0';
119 gotnewline=1;
120 }
121 if( !gotnewline ) {
122 int c = ' ';
123 do {
124 c=fgetc(pinentry.in);
125 } while( c!=-1 && c!='\n' );
126 }
127 // printf("S: %s%s", ret, gotnewline?"\n":" <truncated>\n");
128 return !gotnewline;
129}
130
131void pinentry_seterror(pipe2_t pinentry, const char *s)
132{
133 fprintf(pinentry.out,"SETERROR ");
134 pinentry_send( pinentry, s );
135 char data[4] = { 0 };
136 pinentry_get(pinentry,data,4); // OK
137}
138
139void pinentry_set(pipe2_t pinentry, const char *k, const char *v)
140{
141 fprintf(pinentry.out,"SET%s %s\n", k, v);
142 fflush(pinentry.out);
143 char data[4] = { 0 };
144 pinentry_get(pinentry,data,4); // OK
145}
146
147static const char *arg_prompt = "Passphrase:";
148static const char *arg_desc = "Enter your passphrase to unlock the secret key. ";
149
150int pinentry_getpin(pipe2_t pinentry, char *secret, size_t maxline)
151{
152 char data[MAXLINE] = { 0 };
153
154 // pinentry_send( pinentry, "OPTION lc-ctype=en_US.UTF-8");
155 // pinentry_get( pinentry, data, MAXLINE); // OK
156
157 if (strlen(arg_prompt))
158 pinentry_set( pinentry, "PROMPT", arg_prompt );
159 if (strlen(arg_desc))
160 pinentry_set( pinentry, "DESC", arg_desc );
161
162 pinentry_send( pinentry, "GETPIN");
163 int e = pinentry_get(pinentry, data, MAXLINE);
164 if( strncmp(data,"D ",2)==0 ) {
165 if(!e && strlen(data+2)<=maxline-1 ) {
166 // D <secret>
167 snprintf(secret,maxline,"%s", data+2);
168 //printf("secret = %s\n", secret);
169 pinentry_get(pinentry,data,MAXLINE); // OK
170 return 0;
171 }
172 else {
173 // SECRET TOO BIG, discarding
174 pinentry_seterror(pinentry, "Too big.");
175 return 4;
176 }
177 }
178 else if( strncmp(data,"OK",2)==0 ) {
179 // EMPTY (not an error)
180 secret[0]='\0';
181 //printf("empty secret\n");
182 return 0;
183 }
184 else if( strncmp(data,"ERR ",4)==0 ) {
185 // CANCELED (ERR 83886179 canceled)
186 // ERR 83886246 locale problem
187 // xxx printf("canceled.\n");
188 return 2;
189 }
190 else {
191 // WTF?
192 // xxx printf("Unexpected: %s\n", data);
193 char buf[1024];
194 snprintf(buf,1024,"Unexpected: %s", data );
195 pinentry_seterror(pinentry, buf);
196 return 3;
197 }
198}
199
200pipe2_t do_shell(const char *shell_code) {
201 pipe2_t ret;
202 const char *argv[] = { "/bin/sh", "/bin/sh","-c",NULL,NULL };
203 argv[3]=shell_code;
204 return fopen2(argv);
205}
206
207int validate(pipe2_t pinentry, const char *secret, const char *arg_validate)
208{
209 int retv = 0;
210 if( !arg_validate ) return 1;
211 pipe2_t validate_script = do_shell( arg_validate );
212
213 fprintf( validate_script.out, "%s\n", secret );
214 fclose( validate_script.out );
215 char error_string[1024] = "Error%0Aunspecified error";
216
217 int c;
218 char *p = error_string;
219 while( p!=&error_string[1024-4] && -1!=(c=getc( validate_script.in )) )
220 switch(c){
221 case '%':
222 case '\n':
223 snprintf(p,4, "%%%0.02X", c);
224 p+=3;
225 break;
226 default:
227 *p++ = c;
228 }
229 *p='\0';
230
231 // pinentry ruins its display if this message ends with a newline
232 while(p-3>error_string && memcmp(&p[-3],"%0A",3)==0 ) p-=3;
233 *p='\0';
234
235 fclose( validate_script.in );
236 waitpid(validate_script.pid, &retv, 0 );
237 if( retv ) {
238 fprintf(stderr, "error_string: %s\n", error_string);
239 pinentry_seterror(pinentry, error_string );
240 }
241 return !retv;
242}
243
244FILE *open_socket(const char *socket_path)
245{
246 int sok = socket(PF_LOCAL, SOCK_STREAM, 0);
247 if( sok<0 ) {
248 return NULL;
249 }
250 struct sockaddr_un address;
251 memset(&address, 0, sizeof(struct sockaddr_un));
252 address.sun_family = AF_UNIX;
253 snprintf(address.sun_path, 108, socket_path);
254 if(connect(sok,
255 (struct sockaddr *) &address,
256 sizeof(struct sockaddr_un)) != 0)
257 {
258 return NULL;
259 }
260 return fdopen(sok,"r+");
261}
262
263const char *arg_socket = NULL;
264const char *arg_resocket = NULL;
265const char *arg_tellsecret = NULL;
266char secret[MAXLINE]; // TODO: secure memory
267
268void tell_encoded_secret(FILE *out)
269{
270 char *encoded_secret = malloc(strlen(secret)*2 + 1);
271 for (char *p = secret; *p; ++p)
272 snprintf(encoded_secret + 2*(p - secret), 3, "%0.02X", *p);
273
274 fprintf(out, "%s\n", encoded_secret);
275 fflush(out);
276 free(encoded_secret);
277}
278
279void signal_usr1(int val)
280{
281 if( !arg_tellsecret ) {
282 fprintf(stderr, "USR1, no action\n");
283 return;
284 } else
285 fprintf(stderr, "Received SIGUSR1; will tell secret.\n");
286
287 pipe2_t tellsecret = do_shell( arg_tellsecret );
288 tell_encoded_secret(tellsecret.out);
289
290 FILE *sock = NULL;
291 if (arg_socket) sock=open_socket(arg_socket);
292
293 for (;;) {
294 char *response = 0;
295 size_t len;
296 if (sock && (getline(&response, &len, sock) > 0)) {
297 fprintf(tellsecret.out, "%s", response);
298 fflush(tellsecret.out);
299 }
300
301 if (getline(&response, &len, tellsecret.in) > 0) {
302 if (sock) {
303 fprintf(sock, "%s", response);
304 fflush(sock);
305 } else {
306 fprintf(stderr, "?> %s", response);
307 }
308 } else {
309 break;
310 }
311 }
312
313 if (sock) fclose(sock);
314 fclose(tellsecret.out);
315 fclose(tellsecret.in);
316 int status = 0;
317 waitpid(tellsecret.pid, &status, 0 );
318}
319
320void signal_usr2(int wat)
321{
322 fprintf(stderr, "Received SIGUSR2; will change socket and emulate SIGUSR1.\n");
323 if (arg_resocket)
324 arg_socket = arg_resocket;
325 signal_usr1(wat);
326}
327
328void signal_term(int val)
329{
330 // TODO: Wipe secret
331 exit(0);
332}
333
334int main(int argc, char **argv)
335{
336 pipe2_t pinentry = { 0 };
337
338 char tty_name_buf[256];
339 if( ttyname_r(1, tty_name_buf, sizeof(tty_name_buf)) ) {
340 tty_name_buf[0]='\0';
341 //error(2,0,"ttyname_r");
342 }
343 const char *arg_ttyname = tty_name_buf;
344 const char *arg_pinentry = "/usr/bin/pinentry-curses";
345 const char *arg_validate = NULL;
346 const char *arg_pidfile = NULL; //same as "/dev/stdout";
347 int tell_at_start=0;
348
349 const char **expect = NULL;
350 int i=0;
351 for(i=1;i<argc;i++) {
352 if(expect) {
353 *expect = argv[i];
354 expect = NULL;
355 }
356 else if( strcmp(argv[i],"--setdesc")==0 )
357 expect = &arg_desc;
358 else if( strcmp(argv[i],"--setprompt")==0 )
359 expect = &arg_prompt;
360 else if( strcmp(argv[i],"--validate")==0 )
361 expect = &arg_validate;
362 else if( strcmp(argv[i],"--ttyname")==0 )
363 expect = &arg_ttyname;
364 else if( strcmp(argv[i],"--pinentry")==0 )
365 expect = &arg_pinentry;
366 else if( strcmp(argv[i],"--pidfile")==0 )
367 expect = &arg_pidfile;
368 else if( strcmp(argv[i],"--tell")==0 )
369 expect = &arg_tellsecret;
370 else if( strcmp(argv[i],"--tell-immediately")==0 ) {
371 expect = &arg_tellsecret;
372 tell_at_start=1;
373 }
374 else if( strcmp(argv[i],"--socket")==0 )
375 expect = &arg_socket;
376 else if( strcmp(argv[i],"--resocket")==0 )
377 expect = &arg_resocket;
378 }
379
380 if(!arg_ttyname || !arg_ttyname[0]) {
381 error(2,0,"unknown tty, use --ttyname");
382 }
383
384 // xxx printf("validate script = \n%s\n", arg_validate );
385 // xxx printf("ttyname = %s\n", arg_ttyname );
386
387 char dummy[123];
388 //fgets(dummy,sizeof(dummy),stdin);
389
390 // pinentry-curses --lc-ctype en_US.UTF-8 --ttyname /dev/pts/5
391 const char *cmd[] = {
392 NULL /* pinentry command */,
393 "pinentry-curses",
394 "--lc-ctype", "en_US.UTF-8",
395 "--ttyname", NULL,
396 0 };
397 cmd[0] = arg_pinentry;
398 cmd[sizeof(cmd)/sizeof(cmd[0]) - 2] = arg_ttyname;
399 for(i=0; cmd[i]; i++ ) {
400 // xxx printf("cmd[%d]=%s\n", i, cmd[i] );
401 }
402
403 char data[MAXLINE];
404
405 pinentry = fopen2(cmd);
406 pinentry_get(pinentry,data,MAXLINE); // OK
407
408 int e=0;
409 while( 0!=(e=pinentry_getpin(pinentry,secret,MAXLINE))
410 || !validate(pinentry,secret, arg_validate) ) {
411 //printf("e=%d\n",e);
412 if(e==2) break; // Canceled
413 }
414
415 fclose(pinentry.out);
416 fclose(pinentry.in);
417
418 if(e) return e;
419
420 if( tell_at_start ) {
421 signal_usr1(0);
422 }
423
424 int pid = fork();
425 if( !pid ) {
426
427 if (isatty(0)) fclose(stdin);
428 if (isatty(1)) fclose(stdout);
429 if (isatty(2)) fclose(stderr);
430 setsid();
431
432 struct sigaction act = { 0 };
433 sigemptyset(&act.sa_mask);
434 sigaddset(&act.sa_mask, SIGTERM);
435 sigaddset(&act.sa_mask, SIGUSR1);
436 sigaddset(&act.sa_mask, SIGUSR2);
437 act.sa_handler = signal_usr1;
438 sigaction( SIGUSR1, &act, NULL );
439 act.sa_handler = signal_usr2;
440 sigaction( SIGUSR2, &act, NULL );
441 act.sa_handler = signal_term;
442 sigaction( SIGTERM, &act, NULL );
443
444 for(;;) {
445 pause();
446 }
447 return 0;
448 }
449
450 FILE *pidfile = arg_pidfile ? fopen(arg_pidfile,"w") : stdout;
451 fprintf(pidfile, "%d\n", pid );
452 if(arg_pidfile) fclose(pidfile);
453
454 return 0;
455}
diff --git a/src/wait_for_files.c b/src/wait_for_files.c
new file mode 100644
index 0000000..3a7f6b5
--- /dev/null
+++ b/src/wait_for_files.c
@@ -0,0 +1,114 @@
1// wait_for_files - Efficiently sleeps until the files specified on the
2// command line exist (or have existed during execution).
3// Linux 2.6.13, GNU C only. Use gcc -std=c99
4// glibc 2.4 or define PRIVATE_INOTIFY_HEADER
5
6// #define PRIVATE_INOTIFY_HEADER "inotifytools/inotify.h"
7
8#include <errno.h>
9#include <error.h>
10#include <libgen.h>
11#include <stdbool.h>
12#include <string.h>
13#ifdef PRIVATE_INOTIFY_HEADER
14#include PRIVATE_INOTIFY_HEADER
15#else
16#include <sys/inotify.h>
17#endif
18#include <sys/ioctl.h>
19#include <sys/select.h>
20#include <sys/stat.h>
21#include <sys/types.h>
22#include <unistd.h>
23#include <stdlib.h>
24
25char *strdup(const char *s); // wtf?
26
27int main(int argc, char **argv)
28{
29 if (argc == 1) return 0;
30 --argc; ++argv;
31
32 int notify = inotify_init();
33 if (notify < 0)
34 error(-1, errno, "inotify_init error");
35
36 struct wdtab_t {
37 char *basename;
38 int wd; // set to 0 when found
39 } wdtab[argc];
40
41 bool done = true;
42 for (int i = 0; i < argc; ++i) {
43 struct wdtab_t *p = &wdtab[i];
44
45 char *dirname_mem;
46 if (!(dirname_mem = strdup(argv[i])) ||
47 !(p->basename = strdup(argv[i])))
48 error(-1, errno, "strdup error");
49 char *dirname_val = dirname(dirname_mem);
50 p->basename = basename(p->basename);
51
52 p->wd = inotify_add_watch(notify, dirname_val, IN_CREATE|IN_MASK_ADD);
53 if (p->wd < 0)
54 error(-1, errno, "inotify_add_watch error (file: %s)", argv[i]);
55 free(dirname_mem);
56
57 struct stat dummy;
58 if (stat(argv[i], &dummy) == 0)
59 p->wd = 0;
60 else
61 done = false;
62 }
63 if (done) return 0;
64
65 for (;;) {
66 fd_set rfds;
67 FD_ZERO(&rfds);
68 FD_SET(notify, &rfds);
69 switch (select(notify+1, &rfds, NULL, NULL, NULL)) {
70 case -1: error(-1, errno, "select error");
71 case 0: continue; // this should never happen
72 }
73
74 // is ioctl FIONREAD even documented?
75 int bytes_ready = 0;
76 if (ioctl(notify, FIONREAD, &bytes_ready) < 0)
77 error(-1, errno, "ioctl FIONREAD (inotify) error");
78 if (bytes_ready % sizeof(struct inotify_event) != 0)
79 // this should never ever ever happen, evar
80 error(-1, 0, "ioctl FIONREAD (inotify) returned bizarre result (%i)",
81 bytes_ready);
82 if (bytes_ready < sizeof(struct inotify_event))
83 continue; // bytes_ready <= 0. this should never happen
84
85 struct inotify_event evs[bytes_ready / sizeof(struct inotify_event)];
86
87 ssize_t r;
88 if ((r = read(notify, evs, sizeof(evs))) != sizeof(evs)) {
89 if (r < 0)
90 error(-1, errno, "read error (inotify)");
91 else
92 error(1, 0, "short read (inotify, got %i, expected %i)",
93 (int) r, (int) sizeof(evs));
94 }
95
96 done = true;
97 for (int j = 0; j < sizeof(evs) / sizeof(evs[0]); ++j) {
98 struct inotify_event *ev = &evs[j];
99 for (int i = 0; i < argc; ++i) {
100 struct wdtab_t *p = &wdtab[i];
101 if (!p->wd)
102 continue;
103 else if ((ev->wd == p->wd) &&
104 (ev->mask|IN_CREATE) &&
105 !strcmp(ev->name, p->basename))
106 {
107 p->wd = 0;
108 } else
109 done = false;
110 }
111 }
112 if (done) return 0;
113 }
114}