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;
(1) Event assignment: Assigning: "use_posix" = "1".
Also see events: [assignment][const][dead_error_condition][dead_error_begin]
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)
(2) Event assignment: Assigning: "use_posix" = "1".
Also see events: [assignment][const][dead_error_condition][dead_error_begin]
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 {
(5) Event dead_error_begin: Execution cannot reach this statement: "rc = my_sysv_sem_create(rb,...".
Also see events: [assignment][assignment][const][dead_error_condition]
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