1 /*
2 ** Zabbix
3 ** Copyright (C) 2001-2021 Zabbix SIA
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 **/
19
20 #include "common.h"
21 #include "ipc.h"
22 #include "log.h"
23
24 extern char *CONFIG_FILE;
25
26 /******************************************************************************
27 * *
28 * Function: zbx_dshm_create *
29 * *
30 * Purpose: creates dynamic shared memory segment *
31 * *
32 * Parameters: shm - [OUT] the dynamic shared memory data *
33 * proj_id - [IN] the project id used to create shared memory *
34 * key *
35 * shm_size - [IN] the inital size (can be 0) *
36 * mutex - [IN] the name of mutex used to synchronize memory *
37 * access *
38 * copy_func - [IN] the function used to copy shared memory *
39 * contents during reallocation *
40 * errmsg - [OUT] the error message *
41 * *
42 * Return value: SUCCEED - the dynamic shared memory segment was created *
43 * successfully. *
44 * FAIL - otherwise. The errmsg contains error message and *
45 * must be freed by the caller. *
46 * *
47 ******************************************************************************/
zbx_dshm_create(zbx_dshm_t * shm,int proj_id,size_t shm_size,ZBX_MUTEX_NAME mutex,zbx_shm_copy_func_t copy_func,char ** errmsg)48 int zbx_dshm_create(zbx_dshm_t *shm, int proj_id, size_t shm_size, ZBX_MUTEX_NAME mutex,
49 zbx_shm_copy_func_t copy_func, char **errmsg)
50 {
51 const char *__function_name = "zbx_dshm_create";
52 key_t shm_key;
53 int ret = FAIL;
54
55 zabbix_log(LOG_LEVEL_DEBUG, "In %s() proj_id:%d size:" ZBX_FS_SIZE_T, __function_name, proj_id,
56 (zbx_fs_size_t)shm_size);
57
58 if (FAIL == zbx_mutex_create_force(&shm->lock, mutex))
59 {
60 *errmsg = zbx_strdup(*errmsg, "cannot create mutex");
61 goto out;
62 }
63
64 if (0 < shm_size)
65 {
66 if (-1 == (shm_key = zbx_ftok(CONFIG_FILE, proj_id)))
67 {
68 *errmsg = zbx_strdup(*errmsg, "cannot create IPC key");
69 goto out;
70 }
71
72 if (-1 == (shm->shmid = zbx_shmget(shm_key, shm_size)))
73 {
74 *errmsg = zbx_strdup(*errmsg, "cannot allocate shared memory");
75 goto out;
76 }
77 }
78 else
79 shm->shmid = ZBX_NONEXISTENT_SHMID;
80
81 shm->proj_id = proj_id;
82 shm->size = shm_size;
83 shm->copy_func = copy_func;
84
85 ret = SUCCEED;
86 out:
87 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s shmid:%d", __function_name, zbx_result_string(ret), shm->shmid);
88
89 return ret;
90 }
91
92 /******************************************************************************
93 * *
94 * Function: zbx_dshm_destroy *
95 * *
96 * Purpose: destroys dynamic shared memory segment *
97 * *
98 * Parameters: shm - [IN] the dynamic shared memory data *
99 * errmsg - [OUT] the error message *
100 * *
101 * Return value: SUCCEED - the dynamic shared memory segment was destroyed *
102 * successfully. *
103 * FAIL - otherwise. The errmsg contains error message and *
104 * must be freed by the caller. *
105 * *
106 ******************************************************************************/
zbx_dshm_destroy(zbx_dshm_t * shm,char ** errmsg)107 int zbx_dshm_destroy(zbx_dshm_t *shm, char **errmsg)
108 {
109 const char *__function_name = "zbx_dshm_destroy";
110 int ret = FAIL;
111
112 zabbix_log(LOG_LEVEL_DEBUG, "In %s() shmid:%d", __function_name, shm->shmid);
113
114 zbx_mutex_destroy(&shm->lock);
115
116 if (ZBX_NONEXISTENT_SHMID != shm->shmid)
117 {
118 if (-1 == shmctl(shm->shmid, IPC_RMID, NULL))
119 {
120 *errmsg = zbx_dsprintf(*errmsg, "cannot remove shared memory: %s", zbx_strerror(errno));
121 goto out;
122 }
123 shm->shmid = ZBX_NONEXISTENT_SHMID;
124 }
125
126 ret = SUCCEED;
127 out:
128 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
129
130 return ret;
131 }
132
133 /******************************************************************************
134 * *
135 * Function: zbx_dshm_lock *
136 * *
137 ******************************************************************************/
zbx_dshm_lock(zbx_dshm_t * shm)138 void zbx_dshm_lock(zbx_dshm_t *shm)
139 {
140 zbx_mutex_lock(&shm->lock);
141 }
142
143 /******************************************************************************
144 * *
145 * Function: zbx_dshm_unlock *
146 * *
147 ******************************************************************************/
zbx_dshm_unlock(zbx_dshm_t * shm)148 void zbx_dshm_unlock(zbx_dshm_t *shm)
149 {
150 zbx_mutex_unlock(&shm->lock);
151 }
152
153 /******************************************************************************
154 * *
155 * Function: zbx_dshm_validate_ref *
156 * *
157 * Purpose: validates local reference to dynamic shared memory segment *
158 * *
159 * Parameters: shm - [IN] the dynamic shared memory data *
160 * shm_ref - [IN/OUT] a local reference to dynamic shared memory *
161 * segment *
162 * errmsg - [OUT] the error message *
163 * *
164 * Return value: SUCCEED - the local reference to dynamic shared memory *
165 * segment was validated successfully and contains *
166 * corret dynamic shared memory segment address *
167 * FAIL - otherwise. The errmsg contains error message and *
168 * must be freed by the caller. *
169 * *
170 * Comments: This function should be called before accessing the dynamic *
171 * shared memory to ensure that the local reference has correct *
172 * address after shared memory allocation/reallocation. *
173 * *
174 ******************************************************************************/
zbx_dshm_validate_ref(const zbx_dshm_t * shm,zbx_dshm_ref_t * shm_ref,char ** errmsg)175 int zbx_dshm_validate_ref(const zbx_dshm_t *shm, zbx_dshm_ref_t *shm_ref, char **errmsg)
176 {
177 const char *__function_name = "zbx_dshm_validate_ref";
178 int ret = FAIL;
179
180 zabbix_log(LOG_LEVEL_TRACE, "In %s() shmid:%d refid:%d", __function_name, shm->shmid, shm_ref->shmid);
181
182 if (shm->shmid != shm_ref->shmid)
183 {
184 if (ZBX_NONEXISTENT_SHMID != shm_ref->shmid)
185 {
186 if (-1 == shmdt((void *)shm_ref->addr))
187 {
188 *errmsg = zbx_dsprintf(*errmsg, "cannot detach shared memory: %s", zbx_strerror(errno));
189 goto out;
190 }
191 shm_ref->addr = NULL;
192 shm_ref->shmid = ZBX_NONEXISTENT_SHMID;
193 }
194
195 if ((void *)(-1) == (shm_ref->addr = shmat(shm->shmid, NULL, 0)))
196 {
197 *errmsg = zbx_dsprintf(*errmsg, "cannot attach shared memory: %s", zbx_strerror(errno));
198 shm_ref->addr = NULL;
199 goto out;
200 }
201
202 shm_ref->shmid = shm->shmid;
203 }
204
205 ret = SUCCEED;
206 out:
207 zabbix_log(LOG_LEVEL_TRACE, "End of %s():%s", __function_name, zbx_result_string(ret));
208
209 return ret;
210 }
211
212 /******************************************************************************
213 * *
214 * Function: zbx_dshm_realloc *
215 * *
216 * Purpose: reallocates dynamic shared memory segment *
217 * *
218 * Parameters: shm - [IN/OUT] the dynamic shared memory data *
219 * size - [IN] the new segment size *
220 * errmsg - [OUT] the error message *
221 * *
222 * Return value: *
223 * SUCCEED - the shared memory segment was successfully reallocated. *
224 * FAIL - otherwise. The errmsg contains error message and must be *
225 * freed by the caller. *
226 * *
227 * Comments: The shared memory segment is reallocated by simply creating *
228 * a new segment and copying the data from old segment by calling *
229 * the copy_data callback function. *
230 * *
231 ******************************************************************************/
zbx_dshm_realloc(zbx_dshm_t * shm,size_t size,char ** errmsg)232 int zbx_dshm_realloc(zbx_dshm_t *shm, size_t size, char **errmsg)
233 {
234 const char *__function_name = "zbx_dshm_realloc";
235 key_t shm_key;
236 int shmid, ret = FAIL;
237 void *addr, *addr_old = NULL;
238 size_t shm_size;
239
240 zabbix_log(LOG_LEVEL_DEBUG, "In %s() shmid:%d size:" ZBX_FS_SIZE_T, __function_name, shm->shmid,
241 (zbx_fs_size_t)size);
242
243 /* Create the new shared memory segment. The same key is used. */
244 if (-1 == (shm_key = zbx_ftok(CONFIG_FILE, shm->proj_id)))
245 {
246 *errmsg = zbx_strdup(NULL, "cannot create IPC key");
247 goto out;
248 }
249
250 shm_size = ZBX_SIZE_T_ALIGN8(size);
251
252 /* attach to the old segment if possible */
253 if (ZBX_NONEXISTENT_SHMID != shm->shmid && (void *)(-1) == (addr_old = shmat(shm->shmid, NULL, 0)))
254 {
255 *errmsg = zbx_dsprintf(*errmsg, "cannot attach current shared memory: %s", zbx_strerror(errno));
256 goto out;
257 }
258
259 /* zbx_shmget() will: */
260 /* - see that a shared memory segment with this key exists */
261 /* - mark it for deletion */
262 /* - create a new segment with this key, but with a different id */
263
264 if (-1 == (shmid = zbx_shmget(shm_key, shm_size)))
265 {
266 *errmsg = zbx_strdup(NULL, "cannot allocate shared memory");
267 goto out;
268 }
269
270 shm->size = shm_size;
271 shm->shmid = shmid;
272
273 if ((void *)(-1) == (addr = shmat(shmid, NULL, 0)))
274 {
275 if (NULL != addr_old)
276 (void)shmdt(addr_old);
277
278 *errmsg = zbx_dsprintf(*errmsg, "cannot attach new shared memory: %s", zbx_strerror(errno));
279 goto out;
280 }
281
282 /* copy data from the old segment */
283 shm->copy_func(addr, shm->size, addr_old);
284
285 if (-1 == shmdt((void *)addr))
286 {
287 *errmsg = zbx_strdup(*errmsg, "cannot detach from new shared memory");
288 goto out;
289 }
290
291 /* delete the old segment */
292 if (NULL != addr_old && -1 == shmdt((void *)addr_old))
293 {
294 *errmsg = zbx_strdup(*errmsg, "cannot detach from old shared memory");
295 goto out;
296 }
297
298 ret = SUCCEED;
299 out:
300 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s shmid:%d", __function_name, zbx_result_string(ret), shm->shmid);
301
302 return ret;
303 }
304