aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Albinus2026-02-07 11:32:54 +0100
committerMichael Albinus2026-02-07 11:32:54 +0100
commit89209a83b60c87d97f0c05dbf6cb29ff3cdf3d5a (patch)
tree7eb4fe230b36d619b51eeecd6c0a9868fa268b5b
parente1524740bef6cee52e138a086e43988a16ed703e (diff)
downloademacs-89209a83b60c87d97f0c05dbf6cb29ff3cdf3d5a.tar.gz
emacs-89209a83b60c87d97f0c05dbf6cb29ff3cdf3d5a.zip
Support D-Bus file descriptor manipulation
* doc/misc/dbus.texi (Synchronous Methods): Adapt `dbus-call-method'. (Asynchronous Methods): Adapt `dbus-call-method-asynchronously'. (File Descriptors): New chapter, replaces Inhibitor Locks. * etc/NEWS: Replace "New D-Bus functions to support systemd inhibitor locks" by "Support D-Bus file descriptor manipulation". Presentational fixes and improvements. * lisp/net/dbus.el (dbus-call-method) (dbus-call-method-asynchronously): Adapt docstring. (dbus-list-hash-table): Return (nreverse result). (dbus-monitor-goto-serial): Declare `completion'. * src/dbusbind.c (Fdbus_message_internal, xd_read_message_1): Handle `:keep-fd'. (xd_registered_inhibitor_locks, Fdbus_make_inhibitor_lock) (Fdbus_close_inhibitor_lock, Fdbus_registered_inhibitor_locks): Delete. (xd_registered_fds): New variable. (Fdbus__fd_open, Fdbus__fd_close, Fdbus__registered_fds): New DEFUNs. (Bug#79963) (syms_of_dbusbind_for_pdumper): Initialize `xd_registered_fds'. (syms_of_dbusbind): Remove subroutines `Sdbus_make_inhibitor_lock', `Sdbus_close_inhibitor_lock' and `Sdbus_registered_inhibitor_locks'. Remove symbol `Qdbus_call_method'. Declare subroutines `Sdbus__fd_open', `Sdbus__fd_close' and `Sdbus__registered_fds'. Declare symbol `QCkeep_fd'. staticpro `xd_registered_fds'. * test/lisp/net/dbus-tests.el (dbus-test10-inhibitor-locks): Delete. (dbus-test10-keep-fd, dbus-test10-open-close-fd): New tests.
-rw-r--r--doc/misc/dbus.texi151
-rw-r--r--etc/NEWS24
-rw-r--r--lisp/net/dbus.el12
-rw-r--r--src/dbusbind.c188
-rw-r--r--test/lisp/net/dbus-tests.el167
5 files changed, 317 insertions, 225 deletions
diff --git a/doc/misc/dbus.texi b/doc/misc/dbus.texi
index 59685087ae8..5b302c883ad 100644
--- a/doc/misc/dbus.texi
+++ b/doc/misc/dbus.texi
@@ -64,7 +64,7 @@ another. An overview of D-Bus can be found at
64* Alternative Buses:: Alternative buses and environments. 64* Alternative Buses:: Alternative buses and environments.
65* Errors and Events:: Errors and events. 65* Errors and Events:: Errors and events.
66* Monitoring Messages:: Monitoring messages. 66* Monitoring Messages:: Monitoring messages.
67* Inhibitor Locks:: Inhibit system shutdowns and sleep states. 67* File Descriptors:: Handle file descriptors.
68* Index:: Index including concepts, functions, variables. 68* Index:: Index including concepts, functions, variables.
69 69
70* GNU Free Documentation License:: The license for this documentation. 70* GNU Free Documentation License:: The license for this documentation.
@@ -1212,7 +1212,7 @@ which carries the input parameters to the object owning the method to
1212be called, and a reply message returning the resulting output 1212be called, and a reply message returning the resulting output
1213parameters from the object. 1213parameters from the object.
1214 1214
1215@defun dbus-call-method bus service path interface method &optional :timeout timeout :authorizable auth &rest args 1215@defun dbus-call-method bus service path interface method &optional :timeout timeout :authorizable auth :keep-fd &rest args
1216@anchor{dbus-call-method} 1216@anchor{dbus-call-method}
1217This function calls @var{method} on the D-Bus @var{bus}. @var{bus} is 1217This function calls @var{method} on the D-Bus @var{bus}. @var{bus} is
1218either the keyword @code{:system} or the keyword @code{:session}. 1218either the keyword @code{:system} or the keyword @code{:session}.
@@ -1245,6 +1245,11 @@ running):
1245@result{} "/org/freedesktop/systemd1/job/17508" 1245@result{} "/org/freedesktop/systemd1/job/17508"
1246@end lisp 1246@end lisp
1247 1247
1248If the parameter @code{:keep-fd} is given, and the return message has a
1249first argument with a D-Bus type @code{:unix-fd}, the returned file
1250descriptor is kept internally, and can be used in a later call of
1251@code{dbus--close-fd} (@pxref{File Descriptors}).
1252
1248The remaining arguments @var{args} are passed to @var{method} as 1253The remaining arguments @var{args} are passed to @var{method} as
1249arguments. They are converted into D-Bus types as described in 1254arguments. They are converted into D-Bus types as described in
1250@ref{Type Conversion}. 1255@ref{Type Conversion}.
@@ -1324,7 +1329,7 @@ emulate the @code{lshal} command on GNU/Linux systems:
1324@cindex method calls, asynchronous 1329@cindex method calls, asynchronous
1325@cindex asynchronous method calls 1330@cindex asynchronous method calls
1326 1331
1327@defun dbus-call-method-asynchronously bus service path interface method handler &optional :timeout timeout :authorizable auth &rest args 1332@defun dbus-call-method-asynchronously bus service path interface method handler &optional :timeout timeout :authorizable auth :keep-fd &rest args
1328This function calls @var{method} on the D-Bus @var{bus} 1333This function calls @var{method} on the D-Bus @var{bus}
1329asynchronously. @var{bus} is either the keyword @code{:system} or the 1334asynchronously. @var{bus} is either the keyword @code{:system} or the
1330keyword @code{:session}. 1335keyword @code{:session}.
@@ -1347,6 +1352,11 @@ If the parameter @code{:authorizable} is given and the following
1347@var{auth} is non-@code{nil}, the invoked method may interactively 1352@var{auth} is non-@code{nil}, the invoked method may interactively
1348prompt the user for authorization. The default is @code{nil}. 1353prompt the user for authorization. The default is @code{nil}.
1349 1354
1355If the parameter @code{:keep-fd} is given, and the return message has a
1356first argument with a D-Bus type @code{:unix-fd}, the returned file
1357descriptor is kept internally, and can be used in a later call of
1358@code{dbus--close-fd} (@pxref{File Descriptors}).
1359
1350The remaining arguments @var{args} are passed to @var{method} as 1360The remaining arguments @var{args} are passed to @var{method} as
1351arguments. They are converted into D-Bus types as described in 1361arguments. They are converted into D-Bus types as described in
1352@ref{Type Conversion}. 1362@ref{Type Conversion}.
@@ -2205,109 +2215,90 @@ switches to the monitor buffer.
2205@end deffn 2215@end deffn
2206 2216
2207 2217
2208@node Inhibitor Locks 2218@node File Descriptors
2209@chapter Inhibit system shutdowns and sleep states 2219@chapter Handle file descriptors
2210
2211@uref{https://systemd.io/INHIBITOR_LOCKS/, Systemd} includes a logic to
2212inhibit system shutdowns and sleep states. It can be controlled by a
2213D-Bus API@footnote{@uref{https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html}}.
2214Because this API includes handling of file descriptors, not all
2215functions can be implemented by simple D-Bus method calls. Therefore,
2216the following functions are provided.
2217
2218@defun dbus-make-inhibitor-lock what why &optional block
2219This function creates an inhibitor for system shutdowns and sleep states.
2220
2221@var{what} is a colon-separated string of lock types: @samp{shutdown},
2222@samp{sleep}, @samp{idle}, @samp{handle-power-key},
2223@samp{handle-suspend-key}, @samp{handle-hibernate-key},
2224@samp{handle-lid-switch}. Example: @samp{shutdown:idle}.
2225
2226@c@var{who} is a descriptive string of who is taking the lock. If it is
2227@c@code{nil}, it defaults to @samp{Emacs}.
2228 2220
2229@var{why} is a descriptive string of why the lock is taken. Example: 2221Methods offered by the D-Bus API could return a file descriptor, which
2230@samp{Package Update in Progress}. 2222must be handled further. This is indicated by the @code{:keep-fd}
2223parameter when calling the method (@pxref{dbus-call-method}).
2231 2224
2232The optional @var{block} is the mode of the inhibitor lock, either 2225For example, @uref{https://systemd.io/INHIBITOR_LOCKS/, Systemd}
2233@samp{block} (@var{block} is non-@code{nil}), or @samp{delay}. 2226includes a logic to inhibit system shutdowns and sleep states. It can
2234 2227be controlled by a the method @samp{Inhibit} of interface
2235Note, that the @code{who} argument of the inhibitor lock object of the 2228@samp{org.freedesktop.login1.Manager}@footnote{@uref{https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.login1.html}}.
2236systemd manager is always set to the string @samp{Emacs}. 2229This function returns a file descriptor, which must be used to unlock
2237 2230the locked resource, some of which lock the system. In order to keep
2238It returns a file descriptor or @code{nil}, if the lock cannot be 2231this file descriptor internally, the respective D-Bus method call looks
2239acquired. If there is already an inhibitor lock for the triple 2232like (@var{what}, @var{who}, @var{why} and @var{mode} are
2240@code{(WHAT WHY BLOCK)}, this lock is returned. Example: 2233method-specific string arguments)
2241 2234
2242@lisp 2235@lisp
2243(dbus-make-inhibitor-lock "sleep" "Test") 2236(dbus-call-method
2237 :system
2238 "org.freedesktop.login1" "/org/freedesktop/login1"
2239 "org.freedesktop.login1.Manager" "Inhibit"
2240 :keep-fd WHAT WHO WHY MODE)
2244 2241
2245@result{} 25 2242@result{} 25
2246@end lisp 2243@end lisp
2247@end defun
2248 2244
2249@defun dbus-registered-inhibitor-locks 2245The inhibition lock is unlocked, when the returned file descriptor is
2250Return registered inhibitor locks, an alist. 2246removed from the file system. This cannot be achieved on Lisp level.
2251This allows to check, whether other packages of the running Emacs 2247Therefore, there is the function @code{dbus--fd-close} to performs this
2252instance have acquired an inhibitor lock as well. 2248task (see below).
2249
2250@strong{Note}: When the Emacs process itself dies, all such locks are
2251released.
2253 2252
2254An entry in this list is a list @code{(@var{fd} @var{what} @var{why} 2253@strong{Note}: The following functions are internal to the D-Bus
2255@var{block})}. The car of the list is the file descriptor retrieved 2254implementation of Emacs. Use them with care.
2256from a @code{dbus-make-inhibitor-lock} call. The cdr of the list 2255
2257represents the three arguments @code{dbus-make-inhibitor-lock} was 2256@defun dbus--fd-open filename
2258called with. Example: 2257Open @var{filename} and return the respective read-only file descriptor.
2258This is another function to keep a file descriptor internally. The
2259returned file descriptor can be closed by @code{dbus--fd-close}.
2260Example:
2259 2261
2260@lisp 2262@lisp
2261(dbus-registered-inhibitor-locks) 2263(dbus--fd-open "~/.emacs")
2262 2264
2263@result{} ((25 "sleep" "Test" nil)) 2265@result{} 20
2264@end lisp 2266@end lisp
2265@end defun 2267@end defun
2266 2268
2267@defun dbus-close-inhibitor-lock lock 2269@defun dbus--fd-close fd
2268Close inhibitor lock file descriptor. 2270Close file descriptor @var{fd}.
2269 2271@var{fd} must be the result of a @code{dbus-call-method} or
2270@var{lock}, a file descriptor, must be the result of a 2272@code{dbus--fd-open} call, see @code{dbus--registered-fds}. It returns
2271@code{dbus-make-inhibitor-lock} call. It returns @code{t} in case of 2273@code{t} in case of success, or @code{nil} if it isn’t be possible to
2272success, or @code{nil} if it isn't be possible to close the lock, or if 2274close the file descriptor, or if the file descriptor is closed already.
2273the lock is closed already. Example: 2275Example:
2274 2276
2275@lisp 2277@lisp
2276(dbus-close-inhibitor-lock 25) 2278(dbus--fd-close 25)
2277 2279
2278@result{} t 2280@result{} t
2279
2280@end lisp 2281@end lisp
2281@end defun 2282@end defun
2282 2283
2283A typical scenario for these functions is to register for the 2284@defun dbus--registered-fds
2284D-Bus signal @samp{org.freedesktop.login1.Manager.PrepareForSleep}: 2285Return registered file descriptors, an alist.
2286The key is an open file descriptor, retrieved via
2287@code{dbus-call-method} or @code{dbus--open-fd}. The value is a string
2288@var{object-path} or @var{filename}, which represents the arguments the
2289function was called with. Those values are not needed for further
2290operations; they are just shown for information.
2285 2291
2286@lisp 2292This alist allows to check, whether other packages of the running Emacs
2287(defvar my-inhibitor-lock 2293instance have acquired a file descriptor as well. Example:
2288 (dbus-make-inhibitor-lock "sleep" "Test"))
2289 2294
2290(defun my-dbus-PrepareForSleep-handler (start) 2295@lisp
2291 (if start ;; The system goes down for sleep 2296(dbus--registered-fds)
2292 (progn
2293 @dots{}
2294 ;; Release inhibitor lock.
2295 (when (natnump my-inhibitor-lock)
2296 (dbus-close-inhibitor-lock my-inhibitor-lock)
2297 (setq my-inhibitor-lock nil)))
2298 ;; Reacquire inhibitor lock.
2299 (setq my-inhibitor-lock
2300 (dbus-make-inhibitor-lock "sleep" "Test"))))
2301
2302(dbus-register-signal
2303 :system "org.freedesktop.login1" "/org/freedesktop/login1"
2304 "org.freedesktop.login1.Manager" "PrepareForSleep"
2305 #'my-dbus-PrepareForSleep-handler)
2306 2297
2307@result{} ((:signal :system "org.freedesktop.login1.Manager" "PrepareForSleep") 2298@result{} ((20 . "/home/user/.emacs")
2308 ("org.freedesktop.login1" "/org/freedesktop/login1" 2299 (25 . "/org/freedesktop/login1"))
2309 my-dbus-PrepareForSleep-handler))
2310@end lisp 2300@end lisp
2301@end defun
2311 2302
2312 2303
2313@node Index 2304@node Index
diff --git a/etc/NEWS b/etc/NEWS
index 093e525fa81..1d6929f97e2 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -84,9 +84,9 @@ other directory on your system. You can also invoke the
84 84
85+++ 85+++
86** 'line-spacing' now supports specifying spacing above the line. 86** 'line-spacing' now supports specifying spacing above the line.
87Previously, only spacing below the line could be specified. The variable 87Previously, only spacing below the line could be specified. The user
88can now be set to a cons cell to specify spacing both above and below 88option can now be set to a cons cell to specify spacing both above and
89the line, which allows for vertically centering text. 89below the line, which allows for vertically centering text.
90 90
91+++ 91+++
92** 'prettify-symbols-mode' attempts to ignore undisplayable characters. 92** 'prettify-symbols-mode' attempts to ignore undisplayable characters.
@@ -1410,7 +1410,7 @@ is non-nil, this suffix is fontified using 'font-lock-type-face'.
1410 1410
1411--- 1411---
1412*** New user option 'yaml-ts-mode-yamllint-options'. 1412*** New user option 'yaml-ts-mode-yamllint-options'.
1413Additional options for 'yamllint' the command used for Flymake's YAML 1413Additional options for 'yamllint', the command used for Flymake's YAML
1414support. 1414support.
1415 1415
1416** EIEIO 1416** EIEIO
@@ -2629,7 +2629,7 @@ When the argument is non-nil, the function switches to a buffer visiting
2629the directory into which the repository was cloned. 2629the directory into which the repository was cloned.
2630 2630
2631+++ 2631+++
2632*** 'vc-revert' is now bound to '@' in VC-Dir. 2632*** 'vc-revert' is now bound to '@' in VC Directory.
2633 2633
2634+++ 2634+++
2635*** 'vc-revert' is now additionally bound to 'C-x v @'. 2635*** 'vc-revert' is now additionally bound to 'C-x v @'.
@@ -2771,7 +2771,7 @@ base with the remote branch, including uncommitted changes.
2771('vc-root-log-outgoing-base') show the corresponding revision logs. 2771('vc-root-log-outgoing-base') show the corresponding revision logs.
2772These are useful to view all outstanding (unmerged, unpushed) changes on 2772These are useful to view all outstanding (unmerged, unpushed) changes on
2773the current branch. They are also available as 'T =', 'T D', 'T l' and 2773the current branch. They are also available as 'T =', 'T D', 'T l' and
2774'T L' in VC-Dir buffers. 2774'T L' in VC Directory buffers.
2775 2775
2776+++ 2776+++
2777*** New user option 'vc-use-incoming-outgoing-prefixes'. 2777*** New user option 'vc-use-incoming-outgoing-prefixes'.
@@ -3858,11 +3858,13 @@ and 'dbus-call-method-asynchronously' to allow the user to interactively
3858authorize the invoked D-Bus method (for example via polkit). 3858authorize the invoked D-Bus method (for example via polkit).
3859 3859
3860+++ 3860+++
3861*** New D-Bus functions to support systemd inhibitor locks. 3861*** Support D-Bus file descriptor manipulation.
3862The functions 'dbus-make-inhibitor-lock', 'dbus-close-inhibitor-lock' 3862A new ':keep-fd' parameter has been added to 'dbus-call-method' and
3863and 'dbus-registered-inhibitor-locks' implement acquiring and releasing 3863'dbus-call-method-asynchronously' to instruct D-Bus to keep a file
3864systemd inhibitor locks. See the Info node "(dbus) Inhibitor Locks" for 3864descriptor, which has been returned by a method call, internally. The
3865details. 3865functions 'dbus--fd-open', 'dbus--fd-close' and 'dbus--registered-fds'
3866implement managing these file descriptors. See the Info node "(dbus)
3867File Descriptors" for details.
3866 3868
3867** The customization group 'wp' has been removed. 3869** The customization group 'wp' has been removed.
3868It has been obsolete since Emacs 26.1. Use the group 'text' instead. 3870It has been obsolete since Emacs 26.1. Use the group 'text' instead.
diff --git a/lisp/net/dbus.el b/lisp/net/dbus.el
index 1c8f329fdd7..465de028725 100644
--- a/lisp/net/dbus.el
+++ b/lisp/net/dbus.el
@@ -319,6 +319,10 @@ If the parameter `:authorizable' is given and the following AUTH
319is non-nil, the invoked method may interactively prompt the user 319is non-nil, the invoked method may interactively prompt the user
320for authorization. The default is nil. 320for authorization. The default is nil.
321 321
322If the parameter `:keep-fd' is given, and the return message has a first
323argument with a D-Bus type `:unix-fd', the returned file desriptor is
324kept internally, and can be used in a later `dbus--close-fd' call.
325
322All other arguments ARGS are passed to METHOD as arguments. They are 326All other arguments ARGS are passed to METHOD as arguments. They are
323converted into D-Bus types via the following rules: 327converted into D-Bus types via the following rules:
324 328
@@ -453,6 +457,10 @@ If the parameter `:authorizable' is given and the following AUTH
453is non-nil, the invoked method may interactively prompt the user 457is non-nil, the invoked method may interactively prompt the user
454for authorization. The default is nil. 458for authorization. The default is nil.
455 459
460If the parameter `:keep-fd' is given, and the return message has a first
461argument with a D-Bus type `:unix-fd', the returned file desriptor is
462kept internally, and can be used in a later `dbus--close-fd' call.
463
456All other arguments ARGS are passed to METHOD as arguments. They are 464All other arguments ARGS are passed to METHOD as arguments. They are
457converted into D-Bus types via the following rules: 465converted into D-Bus types via the following rules:
458 466
@@ -604,6 +612,7 @@ This is an internal function, it shall not be used outside dbus.el."
604 612
605;;; Hash table of registered functions. 613;;; Hash table of registered functions.
606 614
615;; Seems to be unused. Dow we want to keep it?
607(defun dbus-list-hash-table () 616(defun dbus-list-hash-table ()
608 "Return all registered member registrations to D-Bus. 617 "Return all registered member registrations to D-Bus.
609The return value is a list, with elements of kind (KEY . VALUE). 618The return value is a list, with elements of kind (KEY . VALUE).
@@ -613,7 +622,7 @@ hash table."
613 (maphash 622 (maphash
614 (lambda (key value) (push (cons key value) result)) 623 (lambda (key value) (push (cons key value) result))
615 dbus-registered-objects-table) 624 dbus-registered-objects-table)
616 result)) 625 (nreverse result)))
617 626
618(defun dbus-setenv (bus variable value) 627(defun dbus-setenv (bus variable value)
619 "Set the value of the BUS environment variable named VARIABLE to VALUE. 628 "Set the value of the BUS environment variable named VARIABLE to VALUE.
@@ -2098,6 +2107,7 @@ either a method name, a signal name, or an error name."
2098 2107
2099(defun dbus-monitor-goto-serial () 2108(defun dbus-monitor-goto-serial ()
2100 "Goto D-Bus message with the same serial number." 2109 "Goto D-Bus message with the same serial number."
2110 (declare (completion ignore))
2101 (interactive) 2111 (interactive)
2102 (when (mouse-event-p last-input-event) (mouse-set-point last-input-event)) 2112 (when (mouse-event-p last-input-event) (mouse-set-point last-input-event))
2103 (when-let* ((point (get-text-property (point) 'dbus-serial))) 2113 (when-let* ((point (get-text-property (point) 'dbus-serial)))
diff --git a/src/dbusbind.c b/src/dbusbind.c
index 3cf3ec9897e..98adebfb2d4 100644
--- a/src/dbusbind.c
+++ b/src/dbusbind.c
@@ -128,6 +128,8 @@ static bool xd_in_read_queued_messages = 0;
128#endif 128#endif
129 129
130/* Check whether TYPE is a basic DBusType. */ 130/* Check whether TYPE is a basic DBusType. */
131/* TODO: Shouldn't we assume, that recent D-Bus implementations carry
132 HAVE_DBUS_TYPE_IS_VALID and DBUS_TYPE_UNIX_FD? See configure.ac. */
131#ifdef HAVE_DBUS_TYPE_IS_VALID 133#ifdef HAVE_DBUS_TYPE_IS_VALID
132#define XD_BASIC_DBUS_TYPE(type) \ 134#define XD_BASIC_DBUS_TYPE(type) \
133 (dbus_type_is_valid (type) && dbus_type_is_basic (type)) 135 (dbus_type_is_valid (type) && dbus_type_is_basic (type))
@@ -309,6 +311,8 @@ XD_OBJECT_TO_STRING (Lisp_Object object)
309 } \ 311 } \
310 } while (0) 312 } while (0)
311 313
314/* TODO: Shouldn't we assume, that recent D-Bus implementations carry
315 HAVE_DBUS_VALIDATE_*? See configure.ac. */
312#if (HAVE_DBUS_VALIDATE_BUS_NAME || HAVE_DBUS_VALIDATE_PATH \ 316#if (HAVE_DBUS_VALIDATE_BUS_NAME || HAVE_DBUS_VALIDATE_PATH \
313 || HAVE_DBUS_VALIDATE_INTERFACE || HAVE_DBUS_VALIDATE_MEMBER) 317 || HAVE_DBUS_VALIDATE_INTERFACE || HAVE_DBUS_VALIDATE_MEMBER)
314#define XD_DBUS_VALIDATE_OBJECT(object, func) \ 318#define XD_DBUS_VALIDATE_OBJECT(object, func) \
@@ -1034,6 +1038,8 @@ xd_get_connection_address (Lisp_Object bus)
1034} 1038}
1035 1039
1036/* Return the file descriptor for WATCH, -1 if not found. */ 1040/* Return the file descriptor for WATCH, -1 if not found. */
1041/* TODO: Shouldn't we assume, that recent D-Bus implementations carry
1042 HAVE_DBUS_WATCH_GET_UNIX_FD? See configure.ac. */
1037static int 1043static int
1038xd_find_watch_fd (DBusWatch *watch) 1044xd_find_watch_fd (DBusWatch *watch)
1039{ 1045{
@@ -1349,6 +1355,7 @@ usage: (dbus-message-internal &rest REST) */)
1349 dbus_uint32_t serial = 0; 1355 dbus_uint32_t serial = 0;
1350 unsigned int ui_serial; 1356 unsigned int ui_serial;
1351 int timeout = -1; 1357 int timeout = -1;
1358 dbus_bool_t keepfd = FALSE;
1352 ptrdiff_t count, count0; 1359 ptrdiff_t count, count0;
1353 char signature[DBUS_MAXIMUM_SIGNATURE_LENGTH]; 1360 char signature[DBUS_MAXIMUM_SIGNATURE_LENGTH];
1354 1361
@@ -1525,6 +1532,7 @@ usage: (dbus-message-internal &rest REST) */)
1525 timeout = min (XFIXNAT (args[count+1]), INT_MAX); 1532 timeout = min (XFIXNAT (args[count+1]), INT_MAX);
1526 count = count + 2; 1533 count = count + 2;
1527 } 1534 }
1535
1528 /* Check for authorizable parameter. */ 1536 /* Check for authorizable parameter. */
1529 else if (EQ (args[count], QCauthorizable)) 1537 else if (EQ (args[count], QCauthorizable))
1530 { 1538 {
@@ -1542,6 +1550,24 @@ usage: (dbus-message-internal &rest REST) */)
1542 1550
1543 count = count + 2; 1551 count = count + 2;
1544 } 1552 }
1553
1554 /* Check for keepfd parameter. */
1555 else if (EQ (args[count], QCkeep_fd))
1556 {
1557 if (mtype != DBUS_MESSAGE_TYPE_METHOD_CALL)
1558 XD_SIGNAL1
1559 (build_string (":keep-fd is only supported on method calls"));
1560
1561 /* Ignore this keyword if unsupported. */
1562#ifdef DBUS_TYPE_UNIX_FD
1563 keepfd = TRUE;
1564#else
1565 XD_DEBUG_MESSAGE (":keep-fd not supported");
1566#endif
1567
1568 ++count;
1569 }
1570
1545 else break; 1571 else break;
1546 1572
1547 } 1573 }
@@ -1595,7 +1621,8 @@ usage: (dbus-message-internal &rest REST) */)
1595 result = list3 (QCserial, bus, INT_TO_INTEGER (serial)); 1621 result = list3 (QCserial, bus, INT_TO_INTEGER (serial));
1596 1622
1597 /* Create a hash table entry. */ 1623 /* Create a hash table entry. */
1598 Fputhash (result, handler, Vdbus_registered_objects_table); 1624 Fputhash (result, keepfd ? Fcons (handler, path) : handler,
1625 Vdbus_registered_objects_table);
1599 } 1626 }
1600 else 1627 else
1601 { 1628 {
@@ -1617,106 +1644,81 @@ usage: (dbus-message-internal &rest REST) */)
1617 return result; 1644 return result;
1618} 1645}
1619 1646
1620/* Alist of registered inhibitor locks for D-Bus. 1647/* Alist of registered file descriptors for D-Bus.
1621 An entry in this list is a list (FD WHAT WHY BLOCK). 1648 The key is an open file descriptor, retrieved via `dbus-call-method'
1622 The car of the list is a file descriptor retrieved from a 1649 or `dbus--open-fd'. The value is a string OBJECT-PATH or FILENAME,
1623 'dbus-make-inhibitor-lock` call. The cdr of the list represents the 1650 which represents the arguments the function was called with. Those
1624 three arguments 'dbus-make-inhibitor-lock` was called with. */ 1651 values are not needed for further operations; they are just shown for
1625static Lisp_Object xd_registered_inhibitor_locks; 1652 information. */
1626 1653static Lisp_Object xd_registered_fds;
1627DEFUN ("dbus-make-inhibitor-lock", Fdbus_make_inhibitor_lock, 1654
1628 Sdbus_make_inhibitor_lock, 1655DEFUN ("dbus--fd-open", Fdbus__fd_open, Sdbus__fd_open, 1, 1, 0,
1629 2, 3, 0, 1656 doc: /* Open FILENAME and return the respective read-only file descriptor. */)
1630 doc: /* Inhibit system shutdowns and sleep states. 1657 (Lisp_Object filename)
1631
1632WHAT is a colon-separated string of lock types, i.e. "shutdown",
1633"sleep", "idle", "handle-power-key", "handle-suspend-key",
1634"handle-hibernate-key", "handle-lid-switch". Example: "shutdown:idle".
1635
1636WHY is a descriptive string of why the lock is taken. Example: "Package
1637Update in Progress".
1638
1639The optional BLOCK is the mode of the inhibitor lock, either "block"
1640(BLOCK is non-nil), or "delay".
1641
1642It returns a file descriptor or nil, if the lock cannot be acquired. If
1643there is already an inhibitor lock for the triple (WHAT WHY BLOCK), this
1644lock is returned.
1645
1646For details of the arguments, see Info node `(dbus)Inhibitor Locks'. */)
1647 (Lisp_Object what, Lisp_Object why, Lisp_Object block)
1648{ 1658{
1649 CHECK_STRING (what); 1659 CHECK_STRING (filename);
1650 CHECK_STRING (why); 1660 filename = Fexpand_file_name (filename, Qnil);
1651 if (!NILP (block)) 1661 filename = ENCODE_FILE (filename);
1652 block = Qt;
1653 Lisp_Object who = build_string ("Emacs");
1654 Lisp_Object mode =
1655 (NILP (block)) ? build_string ("delay") : build_string ("block");
1656 1662
1657 /* Check, whether it is registered already. */ 1663 /* Check, whether it is registered already. */
1658 Lisp_Object triple = list3 (what, why, block); 1664 Lisp_Object registered = Frassoc (filename, xd_registered_fds);
1659 Lisp_Object registered = Frassoc (triple, xd_registered_inhibitor_locks);
1660 if (!NILP (registered)) 1665 if (!NILP (registered))
1661 return CAR_SAFE (registered); 1666 return CAR_SAFE (registered);
1662 1667
1663 /* Register lock. */ 1668 /* Open file descriptor. */
1664 Lisp_Object lock = 1669 int fd = emacs_open (SSDATA (filename), O_RDONLY, 0);
1665 calln (Qdbus_call_method, QCsystem,
1666 build_string ("org.freedesktop.login1"),
1667 build_string ("/org/freedesktop/login1"),
1668 build_string ("org.freedesktop.login1.Manager"),
1669 build_string ("Inhibit"), what, who, why, mode);
1670
1671 xd_registered_inhibitor_locks =
1672 Fcons (Fcons (lock, triple), xd_registered_inhibitor_locks);
1673 return lock;
1674}
1675 1670
1676DEFUN ("dbus-close-inhibitor-lock", Fdbus_close_inhibitor_lock, 1671 if (fd <= 0)
1677 Sdbus_close_inhibitor_lock, 1672 XD_SIGNAL2 (build_string ("Cannot open file"), filename);
1678 1, 1, 0,
1679 doc: /* Close inhibitor lock file descriptor.
1680 1673
1681LOCK, a file descriptor, must be the result of a `dbus-make-inhibitor-lock' 1674 /* Register file descriptor. */
1682call. It returns t in case of success, or nil if it isn't be possible 1675 xd_registered_fds =
1683to close the lock, or if the lock is closed already. 1676 Fcons (Fcons (INT_TO_INTEGER (fd), filename), xd_registered_fds);
1677 return INT_TO_INTEGER (fd);
1678}
1684 1679
1685For details, see Info node `(dbus)Inhibitor Locks'. */) 1680DEFUN ("dbus--fd-close", Fdbus__fd_close, Sdbus__fd_close, 1, 1, 0,
1686 (Lisp_Object lock) 1681 doc: /* Close file descriptor FD.
1682FD must be the result of a `dbus-call-method' or `dbus--fd-open' call,
1683see `dbus--registered-fds'. It returns t in case of success, or nil if
1684it isn't be possible to close the file descriptor, or if the file
1685descriptor is closed already. */)
1686 (Lisp_Object fd)
1687{ 1687{
1688 CHECK_FIXNUM (lock); 1688 CHECK_FIXNUM (fd);
1689 1689
1690 /* Check, whether it is registered. */ 1690 /* Check, whether it is registered. */
1691 Lisp_Object registered = assoc_no_quit (lock, xd_registered_inhibitor_locks); 1691 Lisp_Object registered = assoc_no_quit (fd, xd_registered_fds);
1692 if (NILP (registered)) 1692 if (NILP (registered))
1693 return Qnil; 1693 return Qnil;
1694 else 1694 else
1695 { 1695 {
1696 xd_registered_inhibitor_locks = 1696 xd_registered_fds = Fdelete (registered, xd_registered_fds);
1697 Fdelete (registered, xd_registered_inhibitor_locks); 1697 return (emacs_close (XFIXNAT (fd)) == 0) ? Qt : Qnil;
1698 return (emacs_close (XFIXNAT (lock)) == 0) ? Qt : Qnil;
1699 } 1698 }
1700} 1699}
1701 1700
1702DEFUN ("dbus-registered-inhibitor-locks", Fdbus_registered_inhibitor_locks, 1701DEFUN ("dbus--registered-fds", Fdbus__registered_fds, Sdbus__registered_fds,
1703 Sdbus_registered_inhibitor_locks,
1704 0, 0, 0, 1702 0, 0, 0,
1705 doc: /* Return registered inhibitor locks, an alist. 1703 doc: /* Return registered file descriptors, an alist.
1706This allows to check, whether other packages of the running Emacs 1704The key is an open file descriptor, retrieved via `dbus-call-method' or
1707instance have acquired an inhibitor lock as well. 1705`dbus--open-fd'. The value is a string OBJECT-PATH or FILENAME, which
1708An entry in this list is a list (FD WHAT WHY BLOCK). 1706represents the arguments the function was called with. Those values are
1709The car of the list is the file descriptor retrieved from a 1707not needed for further operations; they are just shown for information.
1710'dbus-make-inhibitor-lock` call. The cdr of the list represents the 1708
1711three arguments 'dbus-make-inhibitor-lock` was called with. */) 1709This alist allows to check, whether other packages of the running Emacs
1710instance have acquired a file descriptor as well. */)
1712 (void) 1711 (void)
1713{ 1712{
1714 /* We return a copy of xd_registered_inhibitor_locks, in order to 1713 /* We return a copy of xd_registered_fds, in order to protect it
1715 protect it against malicious manipulation. */ 1714 against malicious manipulation. */
1716 Lisp_Object registered = xd_registered_inhibitor_locks; 1715 Lisp_Object registered = xd_registered_fds;
1717 Lisp_Object result = Qnil; 1716 Lisp_Object result = Qnil;
1718 for (; !NILP (registered); registered = CDR_SAFE (registered)) 1717 for (; !NILP (registered); registered = CDR_SAFE (registered))
1719 result = Fcons (Fcopy_sequence (CAR_SAFE (registered)), result); 1718 {
1719 Lisp_Object tem = CAR_SAFE (registered);
1720 result = Fcons (Fcons (CAR_SAFE (tem), CDR_SAFE (tem)), result);
1721 }
1720 return Fnreverse (result); 1722 return Fnreverse (result);
1721} 1723}
1722 1724
@@ -1836,7 +1838,22 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus)
1836 Fremhash (key, Vdbus_registered_objects_table); 1838 Fremhash (key, Vdbus_registered_objects_table);
1837 1839
1838 /* Store the event. */ 1840 /* Store the event. */
1839 xd_store_event (value, args, event_args); 1841 xd_store_event (CONSP (value) ? CAR_SAFE (value) : value, args, event_args);
1842
1843#ifdef DBUS_TYPE_UNIX_FD
1844 /* Check, whether there is a file descriptor to be kept.
1845 value is (handler . path)
1846 args is ((:unix-fd NN) ...) */
1847 if (CONSP (value)
1848 && CONSP (CAR_SAFE (args))
1849 && EQ (CAR_SAFE (CAR_SAFE (args)), QCunix_fd))
1850 {
1851 xd_registered_fds =
1852 Fcons (Fcons (CAR_SAFE (CDR_SAFE (CAR_SAFE (args))),
1853 CDR_SAFE (value)),
1854 xd_registered_fds);
1855 }
1856#endif
1840 } 1857 }
1841 1858
1842 else /* DBUS_MESSAGE_TYPE_METHOD_CALL, DBUS_MESSAGE_TYPE_SIGNAL. */ 1859 else /* DBUS_MESSAGE_TYPE_METHOD_CALL, DBUS_MESSAGE_TYPE_SIGNAL. */
@@ -1972,7 +1989,7 @@ static void
1972syms_of_dbusbind_for_pdumper (void) 1989syms_of_dbusbind_for_pdumper (void)
1973{ 1990{
1974 xd_registered_buses = Qnil; 1991 xd_registered_buses = Qnil;
1975 xd_registered_inhibitor_locks = Qnil; 1992 xd_registered_fds = Qnil;
1976} 1993}
1977 1994
1978void 1995void
@@ -1980,9 +1997,9 @@ syms_of_dbusbind (void)
1980{ 1997{
1981 defsubr (&Sdbus__init_bus); 1998 defsubr (&Sdbus__init_bus);
1982 defsubr (&Sdbus_get_unique_name); 1999 defsubr (&Sdbus_get_unique_name);
1983 defsubr (&Sdbus_make_inhibitor_lock); 2000 defsubr (&Sdbus__fd_open);
1984 defsubr (&Sdbus_close_inhibitor_lock); 2001 defsubr (&Sdbus__fd_close);
1985 defsubr (&Sdbus_registered_inhibitor_locks); 2002 defsubr (&Sdbus__registered_fds);
1986 2003
1987 DEFSYM (Qdbus_message_internal, "dbus-message-internal"); 2004 DEFSYM (Qdbus_message_internal, "dbus-message-internal");
1988 defsubr (&Sdbus_message_internal); 2005 defsubr (&Sdbus_message_internal);
@@ -2007,6 +2024,11 @@ syms_of_dbusbind (void)
2007 /* Lisp symbol for method interactive authorization. */ 2024 /* Lisp symbol for method interactive authorization. */
2008 DEFSYM (QCauthorizable, ":authorizable"); 2025 DEFSYM (QCauthorizable, ":authorizable");
2009 2026
2027 /* Lisp symbol for file descriptor kept. */
2028#ifdef DBUS_TYPE_UNIX_FD
2029 DEFSYM (QCkeep_fd, ":keep-fd");
2030#endif
2031
2010 /* Lisp symbols of D-Bus types. */ 2032 /* Lisp symbols of D-Bus types. */
2011 DEFSYM (QCbyte, ":byte"); 2033 DEFSYM (QCbyte, ":byte");
2012 DEFSYM (QCboolean, ":boolean"); 2034 DEFSYM (QCboolean, ":boolean");
@@ -2143,7 +2165,7 @@ be called when the D-Bus reply message arrives. */);
2143 /* Initialize internal objects. */ 2165 /* Initialize internal objects. */
2144 pdumper_do_now_and_after_load (syms_of_dbusbind_for_pdumper); 2166 pdumper_do_now_and_after_load (syms_of_dbusbind_for_pdumper);
2145 staticpro (&xd_registered_buses); 2167 staticpro (&xd_registered_buses);
2146 staticpro (&xd_registered_inhibitor_locks); 2168 staticpro (&xd_registered_fds);
2147 2169
2148 Fprovide (intern_c_string ("dbusbind"), Qnil); 2170 Fprovide (intern_c_string ("dbusbind"), Qnil);
2149} 2171}
diff --git a/test/lisp/net/dbus-tests.el b/test/lisp/net/dbus-tests.el
index 53ce1929cad..f4dd9e3796b 100644
--- a/test/lisp/net/dbus-tests.el
+++ b/test/lisp/net/dbus-tests.el
@@ -2308,89 +2308,156 @@ The argument EXPECTED-ARGS is a list of expected arguments for the method."
2308 ;; Cleanup. 2308 ;; Cleanup.
2309 (dbus-unregister-service :session dbus--test-service))) 2309 (dbus-unregister-service :session dbus--test-service)))
2310 2310
2311(ert-deftest dbus-test10-inhibitor-locks () 2311(ert-deftest dbus-test10-keep-fd ()
2312 "Check `dbus-*-inhibitor-locks'." 2312 "Check D-Bus `:keep-fd' argument."
2313 :tags '(:expensive-test) 2313 :tags '(:expensive-test)
2314 (skip-unless dbus--test-enabled-system-bus) 2314 (skip-unless dbus--test-enabled-system-bus)
2315 (skip-unless (dbus-ping :system dbus--test-systemd-service 1000)) 2315 (skip-unless (dbus-ping :system dbus--test-systemd-service 1000))
2316 2316
2317 (let (lock1 lock2) 2317 (let ((what "sleep")
2318 (who "Emacs test user")
2319 (why "Test delay")
2320 (mode "delay")
2321 (fd-directory (format "/proc/%d/fd" (emacs-pid)))
2322 lock1 lock2)
2318 ;; Create inhibitor lock. 2323 ;; Create inhibitor lock.
2319 (setq lock1 (dbus-make-inhibitor-lock "sleep" "Test delay")) 2324 (setq lock1
2325 (dbus-call-method
2326 :system dbus--test-systemd-service dbus--test-systemd-path
2327 dbus--test-systemd-manager-interface "Inhibit"
2328 what who why mode))
2320 (should (natnump lock1)) 2329 (should (natnump lock1))
2321 ;; The lock is reported by systemd. 2330 ;; The lock is reported by systemd.
2322 (should 2331 (should
2323 (member 2332 (member
2324 (list "sleep" "Emacs" "Test delay" "delay" (user-uid) (emacs-pid)) 2333 (list what who why mode (user-uid) (emacs-pid))
2325 (dbus-call-method 2334 (dbus-call-method
2326 :system dbus--test-systemd-service dbus--test-systemd-path 2335 :system dbus--test-systemd-service dbus--test-systemd-path
2327 dbus--test-systemd-manager-interface "ListInhibitors"))) 2336 dbus--test-systemd-manager-interface "ListInhibitors")))
2328 ;; The lock is registered internally. 2337 ;; The lock is not registered internally.
2329 (should 2338 (should-not (assoc lock1 (dbus--registered-fds)))
2330 (member
2331 (list lock1 "sleep" "Test delay" nil)
2332 (dbus-registered-inhibitor-locks)))
2333 ;; There exist a file descriptor. 2339 ;; There exist a file descriptor.
2334 (when (file-directory-p (format "/proc/%d/fd" (emacs-pid))) 2340 (when (file-directory-p fd-directory)
2335 (should (file-symlink-p (format "/proc/%d/fd/%d" (emacs-pid) lock1)))) 2341 (should
2336 2342 (file-symlink-p
2337 ;; It is not possible to modify registered inhibitor locks on Lisp level. 2343 (expand-file-name (number-to-string lock1) fd-directory))))
2338 (setcar (assoc lock1 (dbus-registered-inhibitor-locks)) 'malicious)
2339 (should (assoc lock1 (dbus-registered-inhibitor-locks)))
2340 (should-not (assoc 'malicious (dbus-registered-inhibitor-locks)))
2341
2342 ;; Creating it again returns the same inhibitor lock.
2343 (should (= lock1 (dbus-make-inhibitor-lock "sleep" "Test delay")))
2344 2344
2345 ;; Create another inhibitor lock. 2345 ;; Create another inhibitor lock. Keep the file descriptor.
2346 (setq lock2 (dbus-make-inhibitor-lock "sleep" "Test block" 'block)) 2346 (setq lock2
2347 (dbus-call-method
2348 :system dbus--test-systemd-service dbus--test-systemd-path
2349 dbus--test-systemd-manager-interface "Inhibit" :keep-fd
2350 what who why mode))
2347 (should (natnump lock2)) 2351 (should (natnump lock2))
2348 (should-not (= lock1 lock2)) 2352 (should-not (= lock1 lock2))
2349 ;; The lock is reported by systemd. 2353 ;; The lock is reported by systemd.
2350 (should 2354 (should
2351 (member 2355 (member
2352 (list "sleep" "Emacs" "Test block" "block" (user-uid) (emacs-pid)) 2356 (list what who why mode (user-uid) (emacs-pid))
2353 (dbus-call-method 2357 (dbus-call-method
2354 :system dbus--test-systemd-service dbus--test-systemd-path 2358 :system dbus--test-systemd-service dbus--test-systemd-path
2355 dbus--test-systemd-manager-interface "ListInhibitors"))) 2359 dbus--test-systemd-manager-interface "ListInhibitors")))
2356 ;; The lock is registered internally. 2360 ;; The lock is registered internally.
2357 (should 2361 (should
2358 (member 2362 (member
2359 (list lock2 "sleep" "Test block" t) 2363 (cons lock2 dbus--test-systemd-path)
2360 (dbus-registered-inhibitor-locks))) 2364 (dbus--registered-fds)))
2361 ;; There exist a file descriptor. 2365 ;; There exist a file descriptor.
2362 (when (file-directory-p (format "/proc/%d/fd" (emacs-pid))) 2366 (when (file-directory-p fd-directory)
2363 (should (file-symlink-p (format "/proc/%d/fd/%d" (emacs-pid) lock2)))) 2367 (should
2364 2368 (file-symlink-p
2365 ;; Close the first inhibitor lock. 2369 (expand-file-name (number-to-string lock2) fd-directory))))
2366 (should (dbus-close-inhibitor-lock lock1)) 2370
2367 ;; The internal registration has gone. 2371 ;; Create another inhibitor lock via
2368 (should-not 2372 ;; `dbus-call-method-asynchronously'. Keep the file descriptor.
2369 (member 2373 (setq lock1 nil)
2370 (list lock1 "sleep" "Test delay" nil) 2374 (dbus-call-method-asynchronously
2371 (dbus-registered-inhibitor-locks))) 2375 :system dbus--test-systemd-service dbus--test-systemd-path
2372 ;; The file descriptor has been deleted. 2376 dbus--test-systemd-manager-interface "Inhibit"
2373 (when (file-directory-p (format "/proc/%d/fd" (emacs-pid))) 2377 (lambda (lock) (setq lock1 lock)) :keep-fd
2374 (should-not (file-symlink-p (format "/proc/%d/fd/%d" (emacs-pid) lock1)))) 2378 what who why mode)
2375 2379 (with-timeout (1 (dbus--test-timeout-handler))
2376 ;; Closing it again is a noop. 2380 (while (null lock1) (read-event nil nil 0.1)))
2377 (should-not (dbus-close-inhibitor-lock lock1))
2378
2379 ;; Creating it again returns (another?) inhibitor lock.
2380 (setq lock1 (dbus-make-inhibitor-lock "sleep" "Test delay"))
2381 (should (natnump lock1)) 2381 (should (natnump lock1))
2382 (should-not (= lock1 lock2))
2382 ;; The lock is registered internally. 2383 ;; The lock is registered internally.
2383 (should 2384 (should
2384 (member 2385 (member
2385 (list lock1 "sleep" "Test delay" nil) 2386 (cons lock1 dbus--test-systemd-path)
2386 (dbus-registered-inhibitor-locks))) 2387 (dbus--registered-fds)))
2387 ;; There exist a file descriptor. 2388 ;; There exist a file descriptor.
2388 (when (file-directory-p (format "/proc/%d/fd" (emacs-pid))) 2389 (when (file-directory-p fd-directory)
2389 (should (file-symlink-p (format "/proc/%d/fd/%d" (emacs-pid) lock1)))) 2390 (should
2391 (file-symlink-p
2392 (expand-file-name (number-to-string lock1) fd-directory))))
2393
2394 ;; It is not possible to modify registered inhibitor locks on Lisp level.
2395 (setcar (assoc lock1 (dbus--registered-fds)) 'malicious)
2396 (should (assoc lock1 (dbus--registered-fds)))
2397 (should-not (assoc 'malicious (dbus--registered-fds)))
2390 2398
2391 ;; Close the inhibitor locks. 2399 ;; Close the inhibitor locks.
2392 (should (dbus-close-inhibitor-lock lock1)) 2400 (should (dbus--fd-close lock1))
2393 (should (dbus-close-inhibitor-lock lock2)))) 2401 (should (dbus--fd-close lock2))
2402 ;; The internal registration has gone.
2403 (should-not
2404 (member
2405 (cons lock1 dbus--test-systemd-path)
2406 (dbus--registered-fds)))
2407 (should-not
2408 (member
2409 (cons lock2 dbus--test-systemd-path)
2410 (dbus--registered-fds)))
2411 ;; The file descriptors have been deleted.
2412 (when (file-directory-p fd-directory)
2413 (should-not
2414 (file-exists-p (expand-file-name (number-to-string lock1) fd-directory)))
2415 (should-not
2416 (file-exists-p (expand-file-name (number-to-string lock2) fd-directory))))
2417
2418 ;; Closing them again is a noop.
2419 (should-not (dbus--fd-close lock1))
2420 (should-not (dbus--fd-close lock2))))
2421
2422(ert-deftest dbus-test10-open-close-fd ()
2423 "Check D-Bus open/close a file descriptor."
2424 :tags '(:expensive-test)
2425 (skip-unless dbus--test-enabled-system-bus)
2426 (skip-unless (dbus-ping :system dbus--test-systemd-service 1000))
2427
2428 (ert-with-temp-file tmpfile
2429 (let ((fd-directory (format "/proc/%d/fd" (emacs-pid)))
2430 fd)
2431 ;; Create file descriptor.
2432 (setq fd (dbus--fd-open tmpfile))
2433 (should (natnump fd))
2434 ;; The file descriptor is registered internally.
2435 (should (member (cons fd tmpfile) (dbus--registered-fds)))
2436 ;; There exist a file descriptor file.
2437 (when (file-directory-p fd-directory)
2438 (should
2439 (file-symlink-p (expand-file-name (number-to-string fd) fd-directory)))
2440 (should
2441 (string-equal
2442 (file-truename (expand-file-name (number-to-string fd) fd-directory))
2443 tmpfile)))
2444
2445 ;; It is not possible to modify registered file descriptors on Lisp level.
2446 (setcar (assoc fd (dbus--registered-fds)) 'malicious)
2447 (should (assoc fd (dbus--registered-fds)))
2448 (should-not (assoc 'malicious (dbus--registered-fds)))
2449
2450 ;; Close the file descriptor.
2451 (should (dbus--fd-close fd))
2452 ;; The internal registration has gone.
2453 (should-not (member (cons fd tmpfile) (dbus--registered-fds)))
2454 ;; The file descriptor file has been deleted.
2455 (when (file-directory-p fd-directory)
2456 (should-not
2457 (file-exists-p (expand-file-name (number-to-string fd) fd-directory))))
2458
2459 ;; Closing it again is a noop.
2460 (should-not (dbus--fd-close fd)))))
2394 2461
2395(defun dbus-test-all (&optional interactive) 2462(defun dbus-test-all (&optional interactive)
2396 "Run all tests for \\[dbus]." 2463 "Run all tests for \\[dbus]."