blob: b3baf4d4e653bfe1cd3f7dcce5398a8b30d6befa (
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
|
;;; Consfigurator -- Lisp declarative configuration management system
;;; Copyright (C) 2021 Sean Whitton <spwhitton@spwhitton.name>
;;; This file is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3, or (at your option)
;;; any later version.
;;; This file is distributed in the hope that it will be useful,
;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;; You should have received a copy of the GNU General Public License
;;; along with this program. If not, see <http://www.gnu.org/licenses/>.
(in-package :consfigurator.data.ssh-askpass)
(named-readtables:in-readtable :consfigurator)
(defmethod register-data-source
((type (eql :ssh-askpass)) &key iden1-re iden2-re)
"Data source which will attempt to provide any piece of data matching the
CL-PPCRE regular expressions IDEN1-RE and IDEN2-RE, obtaining the data by
using ssh-askpass(1) to prompt the user to input it. Useful for things like
sudo passwords."
(unless (getenv "DISPLAY")
(missing-data-source "DISPLAY not set; cannot launch ssh-askpass(1)."))
;; This cache duplicates CONSFIGURATOR::*STRING-DATA*, but if we don't keep
;; our own cache here, then we would always have to return the current time
;; from the first closure, and then we'd prompt the user for input every
;; time, bypassing CONSFIGURATOR::*STRING-DATA*.
(let ((cache (make-hash-table :test #'equal)))
(cons
(lambda (iden1 iden2)
(and (re:scan iden1-re iden1)
(re:scan iden2-re iden2)
(if-let ((cached (gethash (cons iden1 iden2) cache)))
(data-version cached) (get-universal-time))))
(lambda (iden1 iden2)
(let ((pair (cons iden1 iden2)))
(or (gethash pair cache)
(setf (gethash pair cache)
(loop with msg
for first = (ssh-askpass iden1 iden2 msg)
for second = (ssh-askpass iden1 iden2 "confirm")
if (string= first second)
return (make-instance
'string-data
:string first :mime "text/plain"
:version (get-universal-time)
:iden1 iden1 :iden2 iden2)
else do (setq msg "did not match; try again")))))))))
(defun ssh-askpass (iden1 iden2 &optional note)
(stripln (run-program
(list "ssh-askpass"
(format nil "~A | ~A~:[~; (~:*~A)~]" iden1 iden2 note))
:output :string)))
|