summaryrefslogtreecommitdiffstats
path: root/pkgs/simple/pinentry-urxvt/default.nix
blob: 686820758ec4e358c2ed11dab1af45af4958d158 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
{ lib, mylib, pkgs, ... }@args:

let
  # config cannot be declared in the input attribute set because that would
  # cause callPackage to inject the wrong config.  Instead, get it from ...
  # via args.
  config = args.config or {};

  cfg = eval.config;

  eval = lib.evalModules {
    modules = lib.singleton {
      _file = toString ./default.nix;
      imports = lib.singleton config;
      options = {
        appName = lib.mkOption {
          default = "pinentry-urxvt";
          type = lib.types.str;
        };
        display = lib.mkOption {
          default = null;
          type = lib.types.nullOr lib.types.str;
        };
        xwud.className = lib.mkOption {
          default = "PinentryUrxvtXwudFloat";
          type = lib.types.str;
        };
      };
    };
  };


in

  # pinentry-urxvt - A mechanism for PIN entry utilizing rxvt-unicode
  #
  # This spawns a PIN entry terminal on top of a tinted screenshot of the
  # current display's root window.  The display for spawning the terminal can
  # be predefined, in which case both the current and the predefined display
  # will show the screenshot.
  #
  # The purpose of the screenshot, aside from looking nice, is to prevent entry
  # of the PIN into the wrong window, e.g. by accidentally moving the cursor
  # while typing.  If necessary, the screenshot can be closed by sending 'q',
  # 'Q', or ctrl-c while its focused.
  #
  pkgs.write "pinentry-urxvt" {
    "/bin/pinentry".link = pkgs.writeDash "pinentry-urxvt-wrapper" ''
      set -efu

      trap cleanup EXIT

      cleanup() {
        ${pkgs.utillinux}/bin/kill -- $(${pkgs.coreutils}/bin/cat "$displayers")
        rm "$displayers"
        rm "$screenshot"
      }

      displayers=$(${pkgs.coreutils}/bin/mktemp -t pinentry-urxvt.$$.displayers.XXXXXXXX)
      screenshot=$(${pkgs.coreutils}/bin/mktemp -t pinentry-urxvt.$$.screenshot.XXXXXXXX)

      ${pkgs.xorg.xwd}/bin/xwd -root |
      ${pkgs.imagemagick}/bin/convert xwd:- -fill \#424242 -colorize 80% xwd:"$screenshot"

      display_screenshot() {
        ${pkgs.exec "pinentry-urxvt.display_screenshot" {
            filename = "${pkgs.xorg.xwud}/bin/xwud";
            argv = [
              cfg.xwud.className
              "-noclick"
            ];
        }} < "$screenshot" &
        wait_for_screenshot $! && echo $! >>"$displayers"
      }

      # Wait for the xwud window by trying to intercept the call to munmap().
      # If it cannot be intercepted within 0.1s, assume that attaching strace
      # wasn't fast enough or xwud doesn't call munmap() anymore.  In either
      # case fall back to search the window by class name, assuming there can
      # be only one per display.
      wait_for_screenshot() {
        if ! \
          ${pkgs.coreutils}/bin/timeout 0.1 \
          ${pkgs.strace}/bin/strace -p "$1" -e munmap 2>&1 |
          read -r _
        then
          until ${pkgs.xdotool}/bin/xdotool search \
                    --classname ${mylib.shell.escape cfg.xwud.className}
          do
            ${pkgs.coreutils}/bin/sleep 0.1
          done
        fi
      }

      display_screenshot

      ${lib.optionalString (cfg.display != null) /* sh */ ''
        if test "$DISPLAY" != ${mylib.shell.escape cfg.display}; then
          export DISPLAY=${mylib.shell.escape cfg.display}
          display_screenshot
        fi
      ''}

      exec 3<&0 4>&1 5>&2
      ${pkgs.rxvt_unicode}/bin/urxvt \
        -name ${mylib.shell.escape cfg.appName} \
        -e ${pkgs.writeDash "pinentry-urxvt-tty" ''
          set -efu
          exec 2>&5
          TTY=$(${pkgs.coreutils}/bin/tty)
          while read -r line <&3; do
            case $line in
              'OPTION ttyname='*)
                echo "OPTION ttyname=$TTY"
                ;;
              *)
                echo "$line"
            esac
          done | ${pkgs.pinentry.tty}/bin/pinentry-tty "$@" >&4
        ''} \
        "$@"
    '';
  }