Somewhat experimental integration between Gnus agent and NetworkManager over D-Bus. Kill IMAP connections and unplug Gnus agent when network goes down, plug agent when network comes up.
I find this useful when running Emacs+Gnus on my laptop, where network conditions often change, especially on suspend/resume. It was annoying that Gnus often hung as a result of these events, usually caused by hanging network sub-processes (openssl, gnutls-cli).
Requires Emacs23 with D-Bus bindings and NetworkManager running. Probably only useful if you’re running Emacs on a modern GNU/Linux distribution of some sort.
Related part of my ~/.gnus:
(require 'dbus)
(setq gnus-nm-dbus-registration nil)
(defvar gnus-nm-connected-hook nil
"Functions to run when network is connected.")
(defvar gnus-nm-disconnected-hook nil
"Functions to run when network is disconnected.")
(defun imap-nuke-server-processes()
"Brutally kill running IMAP server background processes. Useful
when Gnus hangs on network outs or changes."
(interactive)
(let ((sm (if gnus-select-method
(cons gnus-select-method gnus-secondary-select-methods)
gnus-secondary-select-methods)))
(while sm
(let ((method (car (car sm)))
(vserv (nth 1 (car sm))))
(when (and (eq 'nnimap method)
(buffer-local-value 'imap-process (get-buffer (nnimap-get-server-buffer vserv))))
(gnus-message 6 "Killing IMAP process for server %s" vserv)
(delete-process (buffer-local-value 'imap-process (get-buffer (nnimap-get-server-buffer vserv))))))
(setq sm (cdr sm)))))
(defun gnus-nm-agent-unplug()
"Kill IMAP server processes and unplug Gnus agent."
(gnus-message 6 "Network is disconnected, unplugging Gnus agent.")
(with-current-buffer gnus-group-buffer
(imap-nuke-server-processes) ; optional, help prevent hangs in IMAP processes when network has gone down.
(gnus-agent-toggle-plugged nil)))
(defun gnus-nm-agent-plug()
"Plug Gnus agent."
(gnus-message 6 "Network is connected, plugging Gnus agent.")
(with-current-buffer gnus-group-buffer
(gnus-agent-toggle-plugged t)))
(defun gnus-nm-state-dbus-signal-handler (nmstate)
"Handles NetworkManager signals and runs appropriate hooks."
(when (and (fboundp 'gnus-alive-p) (gnus-alive-p))
(cond
((or (= 4 nmstate) (= 1 nmstate))
(run-hooks 'gnus-nm-disconnected-hook))
((= 3 nmstate)
(run-hooks 'gnus-nm-connected-hook)))))
(defun gnus-nm-enable()
"Enable integration with NetworkManager."
(interactive)
(when (not gnus-nm-dbus-registration)
(progn (setq gnus-nm-dbus-registration
(dbus-register-signal :system
"org.freedesktop.NetworkManager" "/org/freedesktop/NetworkManager"
"org.freedesktop.NetworkManager" "StateChanged"
'gnus-nm-state-dbus-signal-handler))
(gnus-message 6 "Enabled integration with NetworkManager"))))
(defun gnus-nm-disable()
"Disable integration with NetworkManager."
(interactive)
(when gnus-nm-dbus-registration
(progn (dbus-unregister-object gnus-nm-dbus-registration)
(setq gnus-nm-dbus-registration nil)
(gnus-message 6 "Disabled integration with NetworkManager"))))
;; Add hooks for plugging/unplugging on network state change:
(add-hook 'gnus-nm-connected-hook 'gnus-nm-agent-plug)
(add-hook 'gnus-nm-disconnected-hook 'gnus-nm-agent-unplug)
;; Add hooks for enabling/disabling integration on startup/shutdown:
(add-hook 'gnus-started-hook 'gnus-nm-enable)
(add-hook 'gnus-exit-gnus-hook 'gnus-nm-disable)
If you want to start up Gnus in offline or online state depending on the current network status, you can add a custom Gnus startup function in ~/.emacs, something like this:
(require 'dbus)
(defun nm-is-connected()
(equal 3 (dbus-get-property
:system "org.freedesktop.NetworkManager" "/org/freedesktop/NetworkManager"
"org.freedesktop.NetworkManager" "State")))
(defun switch-to-or-startup-gnus ()
"Switch to Gnus group buffer if it exists, otherwise start Gnus in plugged or unplugged state,
depending on network status."
(interactive)
(if (or (not (fboundp 'gnus-alive-p))
(not (gnus-alive-p)))
(if (nm-is-connected)
(gnus)
(gnus-unplugged))
(switch-to-buffer gnus-group-buffer)
(delete-other-windows)))
– Øyvind Stegard
With Network Manager 0.9 arriving, the nm-states were renumbered (refer to http://projects.gnome.org/NetworkManager//developers/migrating-to-09/spec.html#type-NM_STATE and http://projects.gnome.org/NetworkManager//developers/spec-08.html#type-NM_STATE).
The following adapted functions work fine for me:
(defun gnus-nm-state-dbus-signal-handler (nmstate)
"Handles NetworkManager signals and runs appropriate hooks."
(when (and (fboundp 'gnus-alive-p) (gnus-alive-p))
(cond
((or (= 20 nmstate) (= 10 nmstate)
(= 50 nmstate) (= 60 nmstate))
(run-hooks 'gnus-nm-disconnected-hook))
((= 70 nmstate)
(run-hooks 'gnus-nm-connected-hook)))))
(defun nm-is-connected()
(equal 70 (dbus-get-property
:system "org.freedesktop.NetworkManager" "/org/freedesktop/NetworkManager"
"org.freedesktop.NetworkManager" "State")))
– Jonas Hörsch