Режим демона: Отложить интерактивные подсказки при запуске?

16

(Обратите внимание, что, как говорится в противоположном, этот вопрос не совпадает с вопросом « Как запустить в режиме демона и подавить интерактивные диалоги?» , Поскольку отправитель «ответил» на этот вопрос, исключив причину появления определенного приглашения.)

Я хотел бы знать, существует ли общий способ не emacs --daemonзависать навсегда, ожидая ответа на приглашение, отображаемое в минибуфере, который еще не существует.

Невозможно соединиться с emacsclient, чтобы ответить на эти запросы, потому что сервер не запускается, пока Emacs не завершит последовательность запуска. (Это означает, что если для ALTERNATE_EDITOR задана пустая строка, что приводит к тому, emacsclientчто сервер не может запустить новый демон, вы можете получить несколько демонов Emacs, которые все застряли и ждут.) Я должен killall emacsисправить проблему прежде чем продолжить.

Я могу играть в whack-a-mole с каждой вещью, вызывающей приглашение при запуске, когда я ее идентифицирую (запуская Emacs в режиме, не являющемся демоном, и видя, что он запрашивает), но это не решение, потому что он не может остановить следующий демон. от зависания при запуске по новой причине.

В качестве примера: распространенная причина, по которой он зависал, была после перезагрузки системы или сбоя Emacs, когда первый Emacs после перезагрузки захотел узнать, можно ли украсть файлы блокировки у несуществующего Emacs. Я мог бы исправить это, предложив совет, который всегда отвечал «да» без взаимодействия. Но затем одним из файлов, которые были открыты при сохранении предыдущего сеанса, был файл TRAMP, требующий пароль sudo или SSH, поэтому демон застрял в ожидании запроса пароля. Поэтому я исправляю это, вручную редактируя файл сеанса (с помощью viили emacs -q!), Чтобы удалить файлы, нарушающие работу, но это не мешает этому произойти в следующий раз.

Таким образом, я могу прекратить загрузку моего сеанса автоматически при запуске и изменить его на команду, которую я должен выполнить вручную из моего первого emacsclient. Но если он не загружает мой сеанс в фоновом режиме, поэтому он готов к тому времени, когда я буду его использовать, вся цель демона потеряна!

Итак, что я хотел бы это:

  • (Лучше всего) Какой-то способ отложить запросы минибуфера, пока я не открою emacsclient, продолжая при этом завершать остальную часть инициализации.
  • (ОК) Какой-то способ сделать все запросы минибуфера, которые я еще не советовал, как описано выше, просто вернуть, noесли не запущен emacsclient. Я могу жить с ошибками моих буферов TRAMP до тех пор, пока они в основном работают.

Есть ли способ достичь любой из этих целей?

тройка
источник
Есть ли способ воспроизвести подобные проблемы программно, чтобы сообщество могло их устранить?
Мелиоратус
1
Ну, как я писал в первой строке, данный пример довольно легко исправить : «Доктор, мне больно, когда я делаю это ...» «Тогда не делай этого». Проблема в общем случае. Но простой способ создать проблему - это (read-desktop)запустить при запуске восстановление рабочего стола , а затем, перед запуском emacs --daemon, создать фиктивный файл блокировки, поместив целое число в файл .emacs.desktop.lock (куда поместить этот файл, к сожалению, зависит от вашей конфигурации , но, вероятно, либо ваш homedir, либо ~ / .emacs.d / .
Trey
1
Здесь часто упоминается случай, например: emacs.stackexchange.com/questions/8147/… или emacs.stackexchange.com/questions/31621/… могут предоставить контекст.
Trey
Эта ошибка кажется связанной: Ошибка № 13697 - Способ узнать, может ли Emacs взаимодействовать с пользователем , но, насколько я знаю, над этим никто не работал.
npostavs
@npostavs Спасибо за ссылку - я комментировал ошибку, хотя мне потребовался фальстарт, который я прокомментировал здесь (так как удалил), прежде чем я понял это!
Trey

Ответы:

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-or-np.

Тобиас
источник
3

Не совсем то, что вы просите, но, возможно, решение вашей первоначальной проблемы:

Я хотел бы знать, существует ли общий способ, чтобы emacs --daemon не зависал вечно, ожидая ответа на приглашение, отображаемое в минибуфере, который еще не существует.

Если демон предоставляет вам графическую рамку для ответа на вопросы, возникающие на этапе запуска, вы больше не застреваете.

Код ниже определяет общий совет, my-with-initial-frameкоторый открывает рамку на первом доступном дисплее (например, :0.0).

Этот совет можно легко добавить к запросам команд типа y-or-n-pили read-passwd, как показано ниже.

Простое открытие фрейма дает вам довольно грубую возможность отвечать на запросы в пользовательском интерфейсе. Можно также использовать диалоговое окно для, y-or-n-pно это потребует специальных решений для конкретных команд запросов. Я хотел избежать этого.

Если вы попробуете этот код в вашем файле инициализации, убедитесь, что это первое.

(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-сервера не сработал с первого раза. Первоначально я не заметил, так как я не перезапустил демон. Я исправил это сейчас. Пожалуйста, проверьте снова. Благодарю.
Тобиас
К моей виртуальной Linux не подключен графический терминал, поэтому я не могу запустить xterm. Кроме того, я не думаю, что это решение может работать для тех, кто это делает - если у вас настроен демон для запуска при запуске, он попытается открыть фрейм в верхней части экрана входа в систему, что недопустимо, поэтому аварий.
Trey
Тобиас, извиняюсь, если вышеприведенное прозвучало грубо - я ответил по телефону и, возможно, оборвал себя, поэтому позвольте мне попытаться уточнить: единственное преимущество, которое я вижу, в том, что вы описываете, по сравнению с тем, что вы не используете демон и работаете server-startв конце запуска вместо этого, если у вас чистый запуск, вам не придется ждать. Но ... вам придется подождать, потому что, если я не пойму неправильно, вы не сможете поставить задачу по запуску демона Emacs в сценарий входа в систему, поскольку тогда графический интерфейс пользователя будет недоступен. (И в случае, подобном моему, это никогда не
Трей
@Trey Не могли бы вы присоединиться к чату ?
Тобиас
1

Я думаю, что отложить запросы в целом будет сложно, но изменить Emacs должно быть довольно легко, чтобы такие подсказки немедленно сигнализировали об ошибке.

Не только это, но если вы не можете ответить на эти запросы без большой гимнастики, я думаю, что это квалифицируется как ошибка, поэтому я рекомендую вам представить отчет об ошибке для этого.

Стефан
источник
Я думаю, что мне нужно немного больше деталей. Рассмотрим блокировку файла сохранения на рабочем столе, о которой я упоминал в комментарии к награде выше. Как можно было бы превратить Warning: desktop file appears to be in use by PID xxx. Using it may cause conflicts. Use it anyway? (y or n)приглашение в ошибку, не обращаясь конкретно к «рабочему столу» каким-либо образом (потому что этот способ, будучи необщим, ложным ударом)?
Trey
Стефан, также есть проблема, которая не беспокоит меня, потому что я не запускаю демон Emacs таким образом, но для получения общего полезного ответа, возможно, потребуется обратиться к нему: из-за фатального сбоя Emacs запустится через systemd или другие сторожевые таймеры для перезапуска в петле. Но игнорирование ошибок и просто вход в систему, *Messages*вероятно, является недостаточным хедз-апом при первом подключении клиента, что может привести к серьезным ошибкам и требует немедленного внимания, прежде чем пользователь попытается выполнить какие-либо операции с состоянием.
Trey
(Чтобы уточнить для тех, кто не использует демон - если вы запустите его вручную, либо через, emacs --daemonлибо начав emacsclientс ALTERNATE_EDITORпеременной окружения, для которой задана пустая строка, вы увидите вывод, который обычно отправляется в *Messages*эхо-запрос в терминале, пока демон инициализация завершена, и Emacs готов, но многие Emacs запускают демон при запуске системы или во время входа в систему, а выходные данные либо регистрируются, либо выбрасываются
Trey
1
@Trey: сигнализация об ошибке должна быть не в, desktopа в y-or-n-pфункции (или еще ниже). У нас есть некоторый механизм для задержки отображения ошибок, которые произошли во время запуска, поэтому мы могли бы использовать его для отображения их, когда первый emacsclient подключается к демону.
Стефан
В любом случае, однако, большинство пользователей не просматривает *Messages*- и в то время как малоиспользуемая *Warnings*система действительно открывает окно в буфер, если активный кадр существует, когда генерируется предупреждение, в этом случае нет кадра, и он не Похоже, легко отложить всплывающее окно до первого истощения после проблемы предупреждения. Если бы это могло быть сделано, ваше предложение сделать yes-or-no-pпредупреждение перед клиентом было бы совершенно идеальным. (Я сомневаюсь, что пользователи прочесывают *Messages*при запуске!)
Trey