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