守护程序模式:在启动时是否推迟交互提示?


16

(请注意,与此相反,此问题与“ 如何在守护程序模式下启动并禁止交互对话框?”相同,因为提交者“回答”了该问题,从而消除了导致出现特定提示的原因。)

我想知道是否有一种通用方法可以防止emacs --daemon永远挂起,等待对尚未存在的微型缓冲区中显示的提示的答案。

与emacsclient连接以回答这些提示是不可能的,因为直到Emacs完成启动顺序服务器才启动。(这意味着,如果您将ALTERNATE_EDITOR设置为空字符串,这将导致emacsclient找不到服务器启动新的守护程序,则最终可能会导致多个Emacs守护程序被卡住并等待。)我必须killall emacs解决此问题。在继续之前。

我可以在识别到每件事的过程中对每件事都进行重击(通过在非守护程序模式下启动Emacs并查看其要求),但这不是解决方案,因为它无法停止下一个守护程序因新原因而挂在启动上。

举个例子:当系统重新启动或Emacs崩溃后,它挂起的常见原因是第一次重新启动后,Emacs想知道是否可以从已失效的Emacs窃取锁定文件。我可以通过创建建议来解决该问题,以使该提示始终在没有交互的情况下回答“是”。但是随后,在上一个会话保存中打开的文件之一是需要使用sudo或SSH密码的TRAMP文件,因此,守护程序一直在等待密码提示。因此,我通过手动编辑会话文件(使用viemacs -q!)来删除有问题的文件来解决此问题,但这并不能阻止它下次发生。

因此,我可以在启动时自动停止加载会话,并将其更改为必须从第一个emacsclient手动执行的命令。但是,如果它不是在后台加载我的会话,那么在我准备使用它的时候就已经准备好了,守护程序的全部目的都将丢失!

所以我想要的是:

  • (最佳)以某种方式将最小缓冲区提示延迟到我打开emacsclient时,同时仍完成其余的初始化。
  • (确定)以某种方式使所有我没有告知过的小缓冲区提示,否则如上所述,no除非正在运行emacsclient,否则只是返回。只要它可以正常工作,我就可以忍受TRAMP缓冲区出错。

有什么方法可以实现这些目标之一?


是否有办法以编程方式重现此类问题,以便社区进行故障排除?
Melioratus

1
好吧,正如我在第一行中写道,修正给定的示例非常容易……“医生,当我这样做时会很痛……”“那就不要那样做。” 问题是一般情况。但是,解决此问题的一种简单方法是先通过以下方式启动桌面还原桌面(read-desktop),然后在运行之前emacs --daemon通过将一个整数放入.emacs.desktop.lock中来创建伪造的锁定文件(不幸的是,该文件的放置位置取决于您的配置,但可能或者您的主目录或〜/ .emacs.d /
三分球

1
例如,这是这里经常提到的情况:emacs.stackexchange.com/questions/8147/…emacs.stackexchange.com/questions/31621/…可能提供上下文。
Trey

该错误似乎与之相关:Bug#13697-据我所知,一种方法可以判断Emacs是否可以与用户进行交互,但是没有人对此进行过任何处理。
npostavs

@npostavs感谢您提供的链接-我已经注释了该错误,尽管在我弄清楚这个错误的开始之前,我在这里做了评论(自删除以来)!
Trey

Answers:


2

我们的讨论清除了您没有任何X服务器在运行的情况,这使我的第一个解决方案对您毫无用处。

在下面的内容中,我介绍了适用于文本终端框架的第二种解决方案。

当初始化需要用户通过一个输入时,avoid-initial-terminalEmacs 建议的功能会一直等到打开文本终端框。提示出现在该帧的微型缓冲区中,您可以进行交互式响应。

与代码相关的信息在代码中以注释形式给出。有TODO带有说明的标记,这些标记向您显示在何处插入您自己的配置。当前,其中存在用于验证代码的测试表单。

;; TODO: Do here configure the server if needed.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Startup the server:
;; Analysis of read_from_minibuffer in src/minibuf.c and daemon_type in src/emacs.c
;; shows that daemon-initialized must have run before read-passwd / read-string
;; works on frames. Before it only works on stdin & stdout.
(server-start) ;;< early start
(let ((after-init-time before-init-time))
  (daemon-initialized)) ;; Finalize the daemon, 

(advice-add 'daemon-initialized :override #'ignore)
;;< Ignore `daemon-initialized' after initialization. It may only run once!
;; Now the background emacs is no longer marked as daemon. It just runs the server.

(defun prevent-server-start (&rest _ignore)
  "Prevent starting a server one time after `server-start' has been advised with this."
  (advice-remove 'server-start #'prevent-server-start))

(advice-add 'server-start :override #'prevent-server-start)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Prepare waiting for a real terminal frame when user input is required:

(defun avoid-initial-terminal (fun &rest args)
  "Wait until we are no longer on \"intial-terminal\".
Afterwards run `fun' with frame on the other terminal selected."
  (message "Avoiding initial terminal. Terminal: %S" (get-device-terminal nil))
  (while (string-equal
      (terminal-name (get-device-terminal nil))
      "initial_terminal")
    (sleep-for 1))
  ;; (message "Selected frame: %S; Running %S with %S." (selected-frame) fun args)
  (apply fun args))

(advice-add 'read-string :around #'avoid-initial-terminal)

(advice-add 'read-passwd :around #'avoid-initial-terminal)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; TODO: Your initialization that is not daemon related
;; and may require user input:

;; Currently this is just a test.
(read-passwd "Passwd: ")

(read-string "String: ")

(y-or-n-p "y-or-n query")

测试:Emacs版本:26.1

1)emacs --daemon在控制台上运行。

2)emacsclient --tty在另一个控制台上运行。在此要求您输入密码和字符串。之后,您还需要回答y或np查询。


3

不完全是您的要求,但可能是您原来的问题的解决方案:

我想知道是否有一种通用方法可以防止emacs --daemon永远挂起,以等待显示在尚不存在的minibuffer中的提示的答案。

如果守护程序为您提供了一个图形框架,用于回答启动阶段出现的问题,那么您就不会再受困了。

下面的代码定义了一个一般建议my-with-initial-frame,该建议会在第一个可用显示屏(例如:0.0)上打开一个框架。

该建议可以轻松添加到查询命令(例如y-or-n-p或)中read-passwd,如下所示。

只需打开框架,您就可以相当粗略地回答用户界面上的查询。也可以使用一个对话框,y-or-n-p但这将需要针对特定​​查询命令的特殊解决方案。我想避免这种情况。

如果您在init文件中尝试该代码,请确保它是那里的第一件事。

(when (daemonp)

  (defun my-with-initial-frame (&rest _)
    "Ensure a frame on display :0.0 and ignore args."
    (let* ((display-list (x-display-list))
           (display-re (and display-list (regexp-opt display-list)))
           (term (and display-re (cl-some (lambda (term) (and (string-match display-re (terminal-name term)) term)) (terminal-list))))
           (frame (and term (cl-some (lambda (frame) (and (frame-live-p frame) frame)) (frames-on-display-list term)))))
      (select-frame (or frame (make-frame-on-display (getenv "DISPLAY"))))))

  (message "Advising querying functions with `my-with-initial-frame'.")
  (advice-add 'y-or-n-p :before #'my-with-initial-frame)
  (advice-add 'read-passwd :before #'my-with-initial-frame))

测试:

假设:

有一个正在运行的xserver,程序可以通过该服务器连接 DISPLAY环境变量。

在xterm上输入:

emacs --daemon

emacsclient --eval '(y-or-n-p "A")'

打开带有y-or-n-p查询提示的框架A (y or n)。回答该查询,然后重试:

emacsclient --eval '(y-or-n-p "B")'

B (y or n)在同一框架中带有提示的新查询。关闭该框架,例如,使用,C-x 5 0然后重试:

emacsclient --eval '(y-or-n-p "C")'

一个新的框架随查询提示一起打开 C (y or n)

密码输入也一样。


@Trey我的代码有问题(实际上是测试)。第一次启动x服务器无法正常工作。我最初没有注意,因为我没有重新启动守护程序。我现在纠正了。请再次测试。谢谢。
Tobias

我的Linux虚拟机没有附带图形终端,因此无法运行xterm。另外,我认为该解决方案不适用于那些需要这样做的人—如果您将守护程序设置为在启动时运行,它将尝试在登录屏幕顶部打开一个框架,这是不允许的,因此崩溃。
Trey

Tobias,如果上述听起来很荒唐,我深表歉意。我在电话上回答,可能使自己简短了,所以让我来详细说明一下:我能看到的唯一优点是您不使用守护程序并server-start在启动结束时运行相反,如果您碰巧拥有干净的启动,则无需等待。但是...您将不得不等待,因为除非引起我的误解,否则您无法将启动Emacs守护程序的任务放入系统登录脚本中,因为那时将无法使用GUI。(在像我这样的情况下,也永远不会过去。)
Trey

@Trey您可以加入聊天吗?
Tobias

1

我认为 通常推迟提示会很困难,但是更改Emacs应该相当容易,这样这样的提示会立即表示错误。

不仅如此,而且如果您在没有大量体操的情况下无法回答这些提示,我认为它可以视为错误,因此,我建议您为此提交错误报告。


我想我需要更多细节。考虑一下我在上面的赏金评论中提到的桌面保存文件锁。在以某种方式具体提及“桌面”的情况下Warning: desktop file appears to be in use by PID xxx. Using it may cause conflicts. Use it anyway? (y or n),如何将提示更改为错误(因为那样的话,非一般性的话,会让人感到mol恼)?
Trey

Stefan,还有一个问题并不困扰我,因为我没有以这种方式运行Emacs守护程序,但是要做出通常有用的答案可能需要解决:致命错误将导致Emacs通过systemd或其他看门狗启动在一个循环中。但是,如果忽略错误并仅登录到*Messages*第一个客户端连接上,则可能不足以确保某些问题可能严重严重,需要在用户尝试任何有状态操作之前立即予以注意。
Trey

(为澄清那些不使用守护程序的用户,如果您通过emacs --daemon或通过emacsclientALTERNATE_EDITOR环境变量设置为空字符串手动启动它,您将*Messages*在终端中看到通常回显到守护程序的输出完成初始化和Emacs是准备好,但多有Emacs的启动在系统启动或登录时间的守护进程,并输出logged或扔掉。
特雷

1
@Trey:所述错误信号不应该在desktop但在y-or-n-p功能(或降低尚未)。我们有一些机制可以延迟显示启动过程中发生的错误,因此我们可以使用该机制在第一个emacsclient连接到守护程序时显示它们。
Stefan

但是,在任何一种情况下,大多数用户都不会细读*Messages*- 如果在生成警告时存在活动帧,则很少使用的*Warnings*系统的确会弹出缓冲区窗口在这种情况下,不存在任何帧,并且不会在出现警告问题之后,将弹出窗口推迟到第一个emacsclient似乎并不容易。如果可以做到这一点,那么您提出的向客户yes-or-no-p发出警告的建议将是非常理想的。(我怀疑用户会*Messages*在启动时进行梳理!)
Trey
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.