/src/dbus-broker/src/util/fdlist.c
Line | Count | Source |
1 | | /* |
2 | | * File-Descriptor List |
3 | | * |
4 | | * The FDList object is a small wrapper around a fixed-size array of |
5 | | * file-descriptors. It allows easy handling of file-descriptor sets as atomic |
6 | | * entity, while still providing access to individual entries. |
7 | | * |
8 | | * Furthermore, the FDList object is meant as supplement for AF_UNIX sockets. |
9 | | * Hence, it stores FDs as a cmsghdr entry, ready to be used with sendmsg(2). |
10 | | */ |
11 | | |
12 | | #include <c-stdaux.h> |
13 | | #include <stdlib.h> |
14 | | #include <sys/socket.h> |
15 | | #include "util/error.h" |
16 | | #include "util/fdlist.h" |
17 | | |
18 | | /** |
19 | | * fdlist_new_with_fds() - create fdlist with a set of FDs |
20 | | * @listp: output for new fdlist |
21 | | * @fds: FD array to import |
22 | | * @n_fds: array size of @fds |
23 | | * |
24 | | * This creates a new fdlist and imports the FDs given as @fds + @n_fds. The |
25 | | * FDs are still owned by the caller and will not be freed on fdlist_free(). |
26 | | * |
27 | | * Return: 0 on success, negative error code on failure. |
28 | | */ |
29 | 0 | int fdlist_new_with_fds(FDList **listp, const int *fds, size_t n_fds) { |
30 | 0 | FDList *list; |
31 | |
|
32 | 0 | list = calloc(1, sizeof(*list) + CMSG_SPACE(n_fds * sizeof(int))); |
33 | 0 | if (!list) |
34 | 0 | return error_origin(-ENOMEM); |
35 | | |
36 | 0 | list->consumed = false; |
37 | 0 | list->cmsg->cmsg_len = CMSG_LEN(n_fds * sizeof(int)); |
38 | 0 | list->cmsg->cmsg_level = SOL_SOCKET; |
39 | 0 | list->cmsg->cmsg_type = SCM_RIGHTS; |
40 | 0 | c_memcpy(fdlist_data(list), fds, n_fds * sizeof(int)); |
41 | |
|
42 | 0 | *listp = list; |
43 | 0 | return 0; |
44 | 0 | } |
45 | | |
46 | | /** |
47 | | * fdlist_new_consume_fds() - create fdlist and consume FDs |
48 | | * @listp: output for new fdlist |
49 | | * @fds: FD array to import |
50 | | * @n_fds: array size of @fds |
51 | | * |
52 | | * This is the same as fdlist_new_with_fds() but consumes the FDs. That is, the |
53 | | * caller no longer owns the FDs, and the FDs will be closed on fdlist_free(). |
54 | | * |
55 | | * Return: 0 on success, negative error code on failure. |
56 | | */ |
57 | 0 | int fdlist_new_consume_fds(FDList **listp, const int *fds, size_t n_fds) { |
58 | 0 | int r; |
59 | |
|
60 | 0 | r = fdlist_new_with_fds(listp, fds, n_fds); |
61 | 0 | if (!r) |
62 | 0 | (*listp)->consumed = true; |
63 | |
|
64 | 0 | return r; |
65 | 0 | } |
66 | | |
67 | | /** |
68 | | * fdlist_new_dup_fds() - create fdlist and duplicate FDs |
69 | | * @listp: output for new fdlist |
70 | | * @fds: FD array to duplicate |
71 | | * @n_fds: array size of @fds |
72 | | * |
73 | | * This is the same as fdlist_new_with_fds() but duplicates the FDs. That is, |
74 | | * the caller retains the FDs, and an independent copy of each FD will be owned |
75 | | * by the new object. |
76 | | * |
77 | | * Return: 0 on success, negative error code on failure. |
78 | | */ |
79 | 0 | int fdlist_new_dup_fds(FDList **listp, const int *fds, size_t n_fds) { |
80 | 0 | _c_cleanup_(fdlist_freep) FDList *list = NULL; |
81 | 0 | size_t i; |
82 | 0 | int r, *p; |
83 | |
|
84 | 0 | r = fdlist_new_with_fds(&list, fds, n_fds); |
85 | 0 | if (r) |
86 | 0 | return error_trace(r); |
87 | | |
88 | 0 | p = fdlist_data(list); |
89 | 0 | for (i = 0; i < n_fds; ++i) { |
90 | 0 | p[i] = fcntl(p[i], F_DUPFD_CLOEXEC, 3); |
91 | 0 | if (p[i] < 0) { |
92 | 0 | r = -errno; |
93 | 0 | while (i > 0) { |
94 | 0 | --i; |
95 | 0 | p[i] = c_close(p[i]); |
96 | 0 | } |
97 | 0 | return error_origin(r); |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | 0 | list->consumed = true; |
102 | |
|
103 | 0 | *listp = list; |
104 | 0 | list = NULL; |
105 | 0 | return 0; |
106 | 0 | } |
107 | | |
108 | | /** |
109 | | * fdlist_free() - free fdlist |
110 | | * @list: fdlist to operate on, or NULL |
111 | | * |
112 | | * This frees the fdlist given as @list. If the file-descriptors were marked as |
113 | | * `consumed`, this function will close them. Otherwise, they're left |
114 | | * untouched. |
115 | | * |
116 | | * If @list is NULL, this is a no-op. |
117 | | * |
118 | | * Return: NULL is returned. |
119 | | */ |
120 | 2.98k | FDList *fdlist_free(FDList *list) { |
121 | 2.98k | size_t i, n; |
122 | 2.98k | int *p; |
123 | | |
124 | 2.98k | if (list) { |
125 | 0 | p = fdlist_data(list); |
126 | 0 | n = fdlist_count(list); |
127 | |
|
128 | 0 | if (list->consumed) |
129 | 0 | for (i = 0; i < n; ++i) |
130 | 0 | c_close(p[i]); |
131 | |
|
132 | 0 | free(list); |
133 | 0 | } |
134 | | |
135 | 2.98k | return NULL; |
136 | 2.98k | } |
137 | | |
138 | | /** |
139 | | * fdlist_truncate() - truncate fdlist |
140 | | * @list: fdlist to operate on |
141 | | * @n_fds: number of FDs to retain |
142 | | * |
143 | | * This shrinks the fdlist to size @n_fds. The caller must make sure the fdlist |
144 | | * is sized greater than, or equal to, @n_fds. |
145 | | * |
146 | | * If the discarded FDs were marked as consumed, then this will close them. |
147 | | * Otherwise, they're left untouched. |
148 | | */ |
149 | 0 | void fdlist_truncate(FDList *list, size_t n_fds) { |
150 | 0 | size_t i, n; |
151 | 0 | int *p; |
152 | |
|
153 | 0 | p = fdlist_data(list); |
154 | 0 | n = fdlist_count(list); |
155 | |
|
156 | 0 | c_assert(n_fds <= n); |
157 | | |
158 | 0 | if (list->consumed) |
159 | 0 | for (i = n_fds; i < n; ++i) |
160 | 0 | c_close(p[i]); |
161 | |
|
162 | 0 | list->cmsg->cmsg_len = CMSG_LEN(n_fds * sizeof(int)); |
163 | 0 | } |
164 | | |
165 | | /** |
166 | | * fdlist_steal() - steal FD |
167 | | * @list: fdlist to operate on |
168 | | * @index: index of FD |
169 | | * |
170 | | * This returns the FD at position @index and then drops it form the fdlist, by |
171 | | * replacing it with -1. |
172 | | * |
173 | | * If @index points outside the fdlist range, or if the FD was already stolen, |
174 | | * -1 is returned. |
175 | | * |
176 | | * Return: The FD at position @index is returned. |
177 | | */ |
178 | 0 | int fdlist_steal(FDList *list, size_t index) { |
179 | 0 | int *p, fd = -1; |
180 | |
|
181 | 0 | p = fdlist_data(list); |
182 | |
|
183 | 0 | if (index < fdlist_count(list)) { |
184 | 0 | fd = p[index]; |
185 | 0 | p[index] = -1; |
186 | 0 | } |
187 | |
|
188 | 0 | return fd; |
189 | 0 | } |