Avoid warning about sys/sysctl.h on glibc-2.30.
[paraslash.git] / ipc.c
1 /*
2 * Copyright (C) 2006 Andre Noll <maan@tuebingen.mpg.de>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file ipc.c Inter-process communication and shared memory helpers. */
8
9 #include "para.h"
10 #include "error.h"
11 #include "ipc.h"
12 #include <sys/types.h>
13 #include <sys/param.h>
14
15 #include <sys/ipc.h>
16 #include <sys/shm.h>
17 #include <sys/sem.h>
18
19 /**
20 * Define a new mutex.
21 *
22 * \return The identifier for the new mutex on success, a negative error code
23 * on errors.
24 *
25 * \sa semget(2).
26 */
27 int mutex_new(void)
28 {
29 int ret = semget(IPC_PRIVATE, 1, IPC_CREAT | 0666);
30 return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : ret;
31 }
32
33 /**
34 * Destroy a mutex.
35 *
36 * \param id The identifier of the mutex to be destroyed.
37 *
38 * \return Standard.
39 *
40 * \sa semctl(2)
41 */
42 int mutex_destroy(int id)
43 {
44 int ret = semctl(id, 0, IPC_RMID);
45 return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : 1;
46 }
47
48 static void para_semop(int id, struct sembuf *sops, int num)
49 {
50 do {
51 if (semop(id, sops, num) >= 0)
52 return;
53 } while (errno == EINTR);
54 if (errno == EIDRM) {
55 PARA_CRIT_LOG("semaphore set %d was removed\n", id);
56 return;
57 }
58 PARA_EMERG_LOG("fatal semop error %s: pid %d\n", strerror(errno),
59 (int)getpid());
60 exit(EXIT_FAILURE);
61 }
62
63 /**
64 * Lock the given mutex.
65 *
66 * \param id The identifier of the shared memory area to lock.
67 *
68 * This function either succeeds or aborts.
69 *
70 * \sa semop(2), struct misc_meta_data.
71 */
72 void mutex_lock(int id)
73 {
74 struct sembuf sops[2] = {
75 {
76 .sem_num = 0,
77 .sem_op = 0,
78 .sem_flg = SEM_UNDO
79 },
80 {
81 .sem_num = 0,
82 .sem_op = 1,
83 .sem_flg = SEM_UNDO
84 }
85 };
86 para_semop(id, sops, 2);
87 }
88
89 /**
90 * Unlock a mutex.
91 *
92 * \param id The identifier of the mutex.
93 *
94 * This function either succeeds or aborts.
95 *
96 * \sa semop(2), struct misc_meta_data.
97 */
98 void mutex_unlock(int id)
99 {
100 struct sembuf sops[1] = {
101 {
102 .sem_num = 0,
103 .sem_op = -1,
104 .sem_flg = SEM_UNDO
105 },
106 };
107 para_semop(id, sops, 1);
108 }
109
110 /**
111 * Create a new shared memory area of given size.
112 *
113 * \param size The size of the shared memory area to create.
114 *
115 * \return The id of the shared memory array on success, a negative error
116 * code on errors.
117 *
118 * \sa shmget(2).
119 */
120 int shm_new(size_t size)
121 {
122 int ret = shmget(IPC_PRIVATE, size, IPC_CREAT | IPC_EXCL | 0600);
123 return ret < 0 ? -ERRNO_TO_PARA_ERROR(errno) : ret;
124 }
125
126 /**
127 * Destroy the given shared memory area.
128 *
129 * \param id The shared memory identifier.
130 *
131 * \return The return value of the underlying shmctl() call on success,
132 * a negative error code on errors.
133 *
134 * \sa shmctl(2).
135 */
136 int shm_destroy(int id)
137 {
138 struct shmid_ds shm_desc;
139 int ret = shmctl(id, IPC_RMID, &shm_desc);
140 return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : ret;
141 }
142
143 /**
144 * Attach a shared memory segment.
145 *
146 * \param id The identifier of the shared memory segment to attach.
147 * \param mode Either ATTACH_RO (read only) or ATTACH_RW (read/write).
148 * \param result Points to the attached area just attached on success.
149 *
150 * \return Standard.
151 *
152 * \sa shmat(2).
153 */
154 int shm_attach(int id, enum shm_attach_mode mode, void **result)
155 {
156 if (mode == ATTACH_RW)
157 *result = shmat(id, NULL, 0);
158 else
159 *result = shmat(id, NULL, SHM_RDONLY);
160 return *result == (void *) -1? -ERRNO_TO_PARA_ERROR(errno) : 1;
161 }
162
163 /**
164 * Detach a shared memory segment.
165 *
166 * \param addr The address of the attached segment.
167 *
168 * \return Standard.
169 *
170 * \sa shmdt(2).
171 */
172 int shm_detach(void *addr)
173 {
174 int ret = shmdt(addr);
175 return ret < 0? -ERRNO_TO_PARA_ERROR(errno) : 1;
176 }
177
178 # if defined __FreeBSD__ || defined __NetBSD__
179 #include <sys/sysctl.h>
180 # define SYSCTL_SHMMAX_VARIABLE "kern.ipc.shmmax"
181 # else
182 # undef SYSCTL_SHMMAX_VARIABLE
183 # endif
184
185 /**
186 * Get the maximal size of a shared memory area.
187 *
188 * The value is only computed once when the function is called for the first
189 * time. Subsequent calls return the number which was computed during the
190 * first call.
191 *
192 * \return A number suitable as an argument to \ref shm_new().
193 */
194 size_t shm_get_shmmax(void)
195 {
196 static size_t shmmax;
197
198 if (shmmax > 0) /* only dance once */
199 return shmmax;
200 #ifdef __linux__ /* get it from proc fs */
201 {
202 int fd = open("/proc/sys/kernel/shmmax", O_RDONLY);
203 if (fd >= 0) {
204 char buf[100] = "";
205 int ret = read(fd, buf, sizeof(buf) - 1);
206 if (ret > 0) {
207 buf[ret] = '\0';
208 shmmax = strtoul(buf, NULL, 10);
209 }
210 close(fd);
211 }
212 }
213 #elif defined SYSCTL_SHMMAX_VARIABLE
214 {
215 size_t len = sizeof(shmmax);
216 sysctlbyname(SYSCTL_SHMMAX_VARIABLE, &shmmax, &len, NULL, 0);
217 }
218 #elif defined SHMMAX
219 shmmax = SHMMAX;
220 #endif
221 if (shmmax == 0) {
222 PARA_WARNING_LOG("unable to determine shmmax\n");
223 shmmax = 65535; /* last resort */
224 }
225 PARA_INFO_LOG("shmmax: %zu\n", shmmax);
226 return shmmax;
227 }