1 /*
2 * Copyright (C) 2010 Red Hat, Inc.
3 *
4 * Author: Angus Salkeld <asalkeld@redhat.com>
5 *
6 * This file is part of libqb.
7 *
8 * libqb is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation, either version 2.1 of the License, or
11 * (at your option) any later version.
12 *
13 * libqb is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with libqb. If not, see <http://www.gnu.org/licenses/>.
20 */
21 #include "ringbuffer_int.h"
22 #include <qb/qbdefs.h>
23
24 static int32_t
25 my_posix_sem_timedwait(void * instance, int32_t ms_timeout)
26 {
27 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
28 struct timespec ts_timeout;
29 int32_t res;
30
31 if (ms_timeout > 0) {
32 qb_util_timespec_from_epoch_get(&ts_timeout);
33 qb_timespec_add_ms(&ts_timeout, ms_timeout);
34 }
35
36 sem_wait_again:
37 if (ms_timeout > 0) {
38 res = rpl_sem_timedwait(&rb->shared_hdr->posix_sem, &ts_timeout);
39 } else if (ms_timeout == 0) {
40 res = rpl_sem_trywait(&rb->shared_hdr->posix_sem);
41 } else {
42 res = rpl_sem_wait(&rb->shared_hdr->posix_sem);
43 }
44 if (res == -1) {
45 switch (errno) {
46 case EINTR:
47 goto sem_wait_again;
48 break;
49 case EAGAIN:
50 res = -ETIMEDOUT;
51 break;
52 case ETIMEDOUT:
53 res = -errno;
54 break;
55 default:
56 res = -errno;
57 qb_util_perror(LOG_ERR, "error waiting for semaphore");
58 break;
59 }
60 }
61 return res;
62 }
63
64 static int32_t
65 my_posix_sem_post(void * instance, size_t msg_size)
66 {
67 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
68 if (rpl_sem_post(&rb->shared_hdr->posix_sem) < 0) {
69 return -errno;
70 } else {
71 return 0;
72 }
73 }
74
75 static ssize_t
76 my_posix_getvalue_fn(void * instance)
77 {
78 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
79 int val;
80 if (rpl_sem_getvalue(&rb->shared_hdr->posix_sem, &val) < 0) {
81 return -errno;
82 } else {
83 return val;
84 }
85 }
86
87 static int32_t
88 my_posix_sem_destroy(void * instance)
89 {
90 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
91 qb_enter();
92 if (rpl_sem_destroy(&rb->shared_hdr->posix_sem) == -1) {
93 return -errno;
94 } else {
95 return 0;
96 }
97 }
98
99 static int32_t
100 my_posix_sem_create(void * instance, uint32_t flags)
101 {
102 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
103 int32_t pshared = QB_FALSE;
104 if (flags & QB_RB_FLAG_SHARED_PROCESS) {
105 if ((flags & QB_RB_FLAG_CREATE) == 0) {
106 return 0;
107 }
108 pshared = QB_TRUE;
109 }
110 if (rpl_sem_init(&rb->shared_hdr->posix_sem, pshared, 0) == -1) {
111 return -errno;
112 } else {
113 return 0;
114 }
115 }
116
117 static int32_t
118 my_sysv_sem_timedwait(void * instance, int32_t ms_timeout)
119 {
120 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
121 struct sembuf sops[1];
122 int32_t res = 0;
123 #ifdef HAVE_SEMTIMEDOP
124 struct timespec ts_timeout;
125 struct timespec *ts_pt;
126
127 if (ms_timeout >= 0) {
128 /*
129 * Note: sem_timedwait takes an absolute time where as semtimedop
130 * takes a relative time.
131 */
132 ts_timeout.tv_sec = 0;
133 ts_timeout.tv_nsec = 0;
134 qb_timespec_add_ms(&ts_timeout, ms_timeout);
135 ts_pt = &ts_timeout;
136 } else {
137 ts_pt = NULL;
138 }
139 #endif /* HAVE_SEMTIMEDOP */
140
141 /*
142 * wait for sem post.
143 */
144 sops[0].sem_num = 0;
145 sops[0].sem_op = -1;
146 #ifdef HAVE_SEMTIMEDOP
147 sops[0].sem_flg = 0;
148 #else
149 sops[0].sem_flg = IPC_NOWAIT;
150 #endif /* HAVE_SEMTIMEDOP */
151
152 semop_again:
153 #ifdef HAVE_SEMTIMEDOP
154 if (semtimedop(rb->sem_id, sops, 1, ts_pt) == -1)
155 #else
156 if (semop(rb->sem_id, sops, 1) == -1)
157 #endif /* HAVE_SEMTIMEDOP */
158 {
159 if (errno == EINTR) {
160 goto semop_again;
161 } else if (errno == EAGAIN) {
162 /* make consistent with sem_timedwait */
163 res = -ETIMEDOUT;
164 } else {
165 res = -errno;
166 qb_util_perror(LOG_ERR, "error waiting for semaphore");
167 }
168 return res;
169 }
170 return 0;
171 }
172
173 static int32_t
174 my_sysv_sem_post(void * instance, size_t msg_size)
175 {
176 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
177 struct sembuf sops[1];
178
179 if ((rb->flags & QB_RB_FLAG_SHARED_PROCESS) == 0) {
180 return 0;
181 }
182
183 sops[0].sem_num = 0;
184 sops[0].sem_op = 1;
185 sops[0].sem_flg = 0;
186
187 semop_again:
188 if (semop(rb->sem_id, sops, 1) == -1) {
189 if (errno == EINTR) {
190 goto semop_again;
191 } else {
192 qb_util_perror(LOG_ERR,
193 "could not increment semaphore");
194 }
195
196 return -errno;
197 }
198 return 0;
199 }
200
201 static ssize_t
202 my_sysv_getvalue_fn(void * instance)
203 {
204 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
205 ssize_t res = semctl(rb->sem_id, 0, GETVAL, 0);
206 if (res == -1) {
207 return -errno;
208 }
209 return res;
210 }
211
212 static int32_t
213 my_sysv_sem_destroy(void * instance)
214 {
215 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
216 if (semctl(rb->sem_id, 0, IPC_RMID, 0) == -1) {
217 return -errno;
218 } else {
219 return 0;
220 }
221 }
222
223 static int32_t
224 my_sysv_sem_create(void * instance, uint32_t flags)
225 {
226 struct qb_ringbuffer_s *rb = (struct qb_ringbuffer_s *)instance;
227 union semun options;
228 int32_t res;
229 key_t sem_key;
230
231 sem_key = ftok(rb->shared_hdr->hdr_path, (rb->shared_hdr->word_size + 1));
232
233 if (sem_key == -1) {
234 res = -errno;
235 qb_util_perror(LOG_ERR, "couldn't get a sem id");
236 return res;
237 }
238
239 if (flags & QB_RB_FLAG_CREATE) {
240 rb->sem_id = semget(sem_key, 1, IPC_CREAT | IPC_EXCL | 0600);
241 if (rb->sem_id == -1) {
242 res = -errno;
243 qb_util_perror(LOG_ERR, "couldn't create a semaphore");
244 return res;
245 }
246 options.val = 0;
247 res = semctl(rb->sem_id, 0, SETVAL, options);
248 } else {
249 rb->sem_id = semget(sem_key, 0, 0600);
250 if (rb->sem_id == -1) {
251 res = -errno;
252 qb_util_perror(LOG_ERR, "couldn't get a sem id");
253 return res;
254 }
255 res = 0;
256 }
257 qb_util_log(LOG_DEBUG, "sem key:%d, id:%d, value:%d",
258 (int)sem_key, rb->sem_id, semctl(rb->sem_id, 0, GETVAL, 0));
259
260 return res;
261 }
262
263 int32_t
264 qb_rb_sem_create(struct qb_ringbuffer_s * rb, uint32_t flags)
265 {
266 int32_t rc;
267 int32_t use_posix = QB_TRUE;
268
269 if ((flags & QB_RB_FLAG_SHARED_PROCESS) &&
270 !(flags & QB_RB_FLAG_NO_SEMAPHORE)) {
271 #if defined(HAVE_POSIX_PSHARED_SEMAPHORE) || \
272 defined(HAVE_RPL_PSHARED_SEMAPHORE)
273 use_posix = QB_TRUE;
274 #else
275 #ifdef HAVE_SYSV_PSHARED_SEMAPHORE
276 use_posix = QB_FALSE;
277 #else
278 return -ENOTSUP;
279 #endif /* HAVE_SYSV_PSHARED_SEMAPHORE */
280 #endif /* HAVE_POSIX_PSHARED_SEMAPHORE */
281 }
282 if (flags & QB_RB_FLAG_NO_SEMAPHORE) {
283 rc = 0;
284 rb->notifier.instance = NULL;
285 rb->notifier.timedwait_fn = NULL;
286 rb->notifier.post_fn = NULL;
287 rb->notifier.q_len_fn = NULL;
288 rb->notifier.space_used_fn = NULL;
289 rb->notifier.destroy_fn = NULL;
|
(3) Event const: |
At condition "use_posix", the value of "use_posix" must be equal to 1. |
|
(4) Event dead_error_condition: |
The condition "use_posix" must be true. |
| Also see events: |
[assignment][assignment][dead_error_begin] |
290 } else if (use_posix) {
291 rc = my_posix_sem_create(rb, flags);
292 rb->notifier.instance = rb;
293 rb->notifier.timedwait_fn = my_posix_sem_timedwait;
294 rb->notifier.post_fn = my_posix_sem_post;
295 rb->notifier.q_len_fn = my_posix_getvalue_fn;
296 rb->notifier.space_used_fn = NULL;
297 rb->notifier.destroy_fn = my_posix_sem_destroy;
298 } else {
299 rc = my_sysv_sem_create(rb, flags);
300 rb->notifier.instance = rb;
301 rb->notifier.timedwait_fn = my_sysv_sem_timedwait;
302 rb->notifier.post_fn = my_sysv_sem_post;
303 rb->notifier.q_len_fn = my_sysv_getvalue_fn;
304 rb->notifier.space_used_fn = NULL;
305 rb->notifier.destroy_fn = my_sysv_sem_destroy;
306 }
307 return rc;
308 }
309
310
311 /* For qb_rb_close_helper, we need to open directory in read-only
312 mode and with as lightweight + strict flags as available at
313 given platform (O_PATH for the former, O_DIRECTORY for the
314 latter); end result is available as RB_DIR_RO_FLAGS.
315 */
316 #if defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT)
317 # ifndef O_DIRECTORY
318 # define RB_DIR_RO_FLAGS1 O_RDONLY
319 # else
320 # define RB_DIR_RO_FLAGS1 O_RDONLY|O_DIRECTORY
321 # endif
322 # ifndef O_PATH
323 # define RB_DIR_RO_FLAGS RB_DIR_RO_FLAGS1
324 # else
325 # define RB_DIR_RO_FLAGS RB_DIR_RO_FLAGS1|O_PATH
326 # endif
327
328 int32_t
329 qb_rb_close_helper(struct qb_ringbuffer_s * rb, int32_t unlink_it,
330 int32_t truncate_fallback)
331 {
332 int32_t res = 0, res2 = 0;
333 uint32_t word_size = rb->shared_hdr->word_size;
334 char *hdr_path = rb->shared_hdr->hdr_path;
335
336 if (unlink_it) {
337 qb_util_log(LOG_TRACE, "Free'ing ringbuffer: %s", hdr_path);
338 if (rb->notifier.destroy_fn) {
339 (void)rb->notifier.destroy_fn(rb->notifier.instance);
340 }
341 } else {
342 qb_util_log(LOG_TRACE, "Closing ringbuffer: %s", hdr_path);
343 hdr_path = NULL;
344 }
345
346 if (unlink_it) {
347 char *data_path = rb->shared_hdr->data_path;
348 char *sep = strrchr(data_path, '/');
349 /* we could modify data_path in-situ, but that would segfault if
350 we hadn't write permissions to the underlying mmap'd file */
351 char dir_path[PATH_MAX];
352 int dirfd;
353
354 if (sep != NULL) {
355 strncpy(dir_path, data_path, sep - data_path);
356 dir_path[sep - data_path] = '\0';
357 if ((dirfd = open(dir_path, RB_DIR_RO_FLAGS)) != -1) {
358 res = qb_sys_unlink_or_truncate_at(dirfd, sep + 1,
359 truncate_fallback);
360
361 /* the dirname part is assumed to be the same */
362 if (strncmp(dir_path, hdr_path, sep - data_path)) {
363 qb_util_perror(LOG_DEBUG,
364 "header path is corrupted: %s", hdr_path);
365 res = -ENXIO;
366 }
367
368 sep = hdr_path + (sep - data_path);
369 /* now, don't touch neither data_path nor hdr_path */
370 res2 = qb_sys_unlink_or_truncate_at(dirfd, sep + 1,
371 truncate_fallback);
372 close(dirfd);
373 } else {
374 res = -errno;
375 qb_util_perror(LOG_DEBUG,
376 "Cannot open dir: %s", hdr_path);
377 }
378 } else {
379 res = -EINVAL;
380 qb_util_perror(LOG_DEBUG,
381 "Not dir-separable path: %s", hdr_path);
382 }
383 #else
384 res = qb_sys_unlink_or_truncate(data_path, truncate_fallback);
385 res2 = qb_sys_unlink_or_truncate(hdr_path, truncate_fallback);
386 #endif /* defined(HAVE_OPENAT) && defined(HAVE_UNLINKAT) */
387
388 res = res ? res : res2;
389 hdr_path = NULL;
390 } /* if (unlink_it) */
391
392 if (munmap(rb->shared_data, (word_size * sizeof(uint32_t)) << 1) == -1) {
393 res = res ? res : -errno;
394 qb_util_perror(LOG_DEBUG, "Cannot munmap shared_data");
395 }
396 if (munmap(rb->shared_hdr, sizeof(struct qb_ringbuffer_shared_s)) == -1) {
397 res = res ? res : -errno;
398 qb_util_perror(LOG_DEBUG, "Cannot munmap shared_hdr");
399 }
400 free(rb);
401 return res;
402 }
403