1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2 
3 #include <errno.h>
4 #include <pthread.h>
5 #include <stddef.h>
6 #include <unistd.h>
7 
8 #include "async.h"
9 #include "errno-util.h"
10 #include "fd-util.h"
11 #include "log.h"
12 #include "macro.h"
13 #include "process-util.h"
14 #include "signal-util.h"
15 #include "util.h"
16 
asynchronous_job(void * (* func)(void * p),void * arg)17 int asynchronous_job(void* (*func)(void *p), void *arg) {
18         sigset_t ss, saved_ss;
19         pthread_attr_t a;
20         pthread_t t;
21         int r, k;
22 
23         /* It kinda sucks that we have to resort to threads to implement an asynchronous close(), but well, such is
24          * life. */
25 
26         r = pthread_attr_init(&a);
27         if (r > 0)
28                 return -r;
29 
30         r = pthread_attr_setdetachstate(&a, PTHREAD_CREATE_DETACHED);
31         if (r > 0) {
32                 r = -r;
33                 goto finish;
34         }
35 
36         assert_se(sigfillset(&ss) >= 0);
37 
38         /* Block all signals before forking off the thread, so that the new thread is started with all signals
39          * blocked. This way the existence of the new thread won't affect signal handling in other threads. */
40 
41         r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss);
42         if (r > 0) {
43                 r = -r;
44                 goto finish;
45         }
46 
47         r = pthread_create(&t, &a, func, arg);
48 
49         k = pthread_sigmask(SIG_SETMASK, &saved_ss, NULL);
50 
51         if (r > 0)
52                 r = -r;
53         else if (k > 0)
54                 r = -k;
55         else
56                 r = 0;
57 
58 finish:
59         pthread_attr_destroy(&a);
60         return r;
61 }
62 
asynchronous_sync(pid_t * ret_pid)63 int asynchronous_sync(pid_t *ret_pid) {
64         int r;
65 
66         /* This forks off an invocation of fork() as a child process, in order to initiate synchronization to
67          * disk. Note that we implement this as helper process rather than thread as we don't want the sync() to hang our
68          * original process ever, and a thread would do that as the process can't exit with threads hanging in blocking
69          * syscalls. */
70 
71         r = safe_fork("(sd-sync)", FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS, ret_pid);
72         if (r < 0)
73                 return r;
74         if (r == 0) {
75                 /* Child process */
76                 (void) sync();
77                 _exit(EXIT_SUCCESS);
78         }
79 
80         return 0;
81 }
82 
close_thread(void * p)83 static void *close_thread(void *p) {
84         (void) pthread_setname_np(pthread_self(), "close");
85 
86         assert_se(close_nointr(PTR_TO_FD(p)) != -EBADF);
87         return NULL;
88 }
89 
asynchronous_close(int fd)90 int asynchronous_close(int fd) {
91         int r;
92 
93         /* This is supposed to behave similar to safe_close(), but
94          * actually invoke close() asynchronously, so that it will
95          * never block. Ideally the kernel would have an API for this,
96          * but it doesn't, so we work around it, and hide this as a
97          * far away as we can. */
98 
99         if (fd >= 0) {
100                 PROTECT_ERRNO;
101 
102                 r = asynchronous_job(close_thread, FD_TO_PTR(fd));
103                 if (r < 0)
104                          assert_se(close_nointr(fd) != -EBADF);
105         }
106 
107         return -1;
108 }
109