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 * shm_size - [IN] the initial size (can be 0) *
34 * mutex - [IN] the name of mutex used to synchronize memory *
35 * access *
36 * copy_func - [IN] the function used to copy shared memory *
37 * contents during reallocation *
38 * errmsg - [OUT] the error message *
39 * *
40 * Return value: SUCCEED - the dynamic shared memory segment was created *
41 * successfully. *
42 * FAIL - otherwise. The errmsg contains error message and *
43 * must be freed by the caller. *
44 * *
45 ******************************************************************************/
zbx_dshm_create(zbx_dshm_t * shm,size_t shm_size,zbx_mutex_name_t mutex,zbx_shm_copy_func_t copy_func,char ** errmsg)46 int zbx_dshm_create(zbx_dshm_t *shm, size_t shm_size, zbx_mutex_name_t mutex,
47 zbx_shm_copy_func_t copy_func, char **errmsg)
48 {
49 int ret = FAIL;
50
51 zabbix_log(LOG_LEVEL_DEBUG, "In %s() size:" ZBX_FS_SIZE_T, __func__, (zbx_fs_size_t)shm_size);
52
53 if (SUCCEED != zbx_mutex_create(&shm->lock, mutex, errmsg))
54 goto out;
55
56 if (0 < shm_size)
57 {
58 if (-1 == (shm->shmid = zbx_shm_create(shm_size)))
59 {
60 *errmsg = zbx_strdup(*errmsg, "cannot allocate shared memory");
61 goto out;
62 }
63 }
64 else
65 shm->shmid = ZBX_NONEXISTENT_SHMID;
66
67 shm->size = shm_size;
68 shm->copy_func = copy_func;
69
70 ret = SUCCEED;
71 out:
72 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s shmid:%d", __func__, zbx_result_string(ret), shm->shmid);
73
74 return ret;
75 }
76
77 /******************************************************************************
78 * *
79 * Function: zbx_dshm_destroy *
80 * *
81 * Purpose: destroys dynamic shared memory segment *
82 * *
83 * Parameters: shm - [IN] the dynamic shared memory data *
84 * errmsg - [OUT] the error message *
85 * *
86 * Return value: SUCCEED - the dynamic shared memory segment was destroyed *
87 * successfully. *
88 * FAIL - otherwise. The errmsg contains error message and *
89 * must be freed by the caller. *
90 * *
91 ******************************************************************************/
zbx_dshm_destroy(zbx_dshm_t * shm,char ** errmsg)92 int zbx_dshm_destroy(zbx_dshm_t *shm, char **errmsg)
93 {
94 int ret = FAIL;
95
96 zabbix_log(LOG_LEVEL_DEBUG, "In %s() shmid:%d", __func__, shm->shmid);
97
98 zbx_mutex_destroy(&shm->lock);
99
100 if (ZBX_NONEXISTENT_SHMID != shm->shmid)
101 {
102 if (-1 == shmctl(shm->shmid, IPC_RMID, NULL))
103 {
104 *errmsg = zbx_dsprintf(*errmsg, "cannot remove shared memory: %s", zbx_strerror(errno));
105 goto out;
106 }
107 shm->shmid = ZBX_NONEXISTENT_SHMID;
108 }
109
110 ret = SUCCEED;
111 out:
112 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __func__, zbx_result_string(ret));
113
114 return ret;
115 }
116
117 /******************************************************************************
118 * *
119 * Function: zbx_dshm_lock *
120 * *
121 ******************************************************************************/
zbx_dshm_lock(zbx_dshm_t * shm)122 void zbx_dshm_lock(zbx_dshm_t *shm)
123 {
124 zbx_mutex_lock(shm->lock);
125 }
126
127 /******************************************************************************
128 * *
129 * Function: zbx_dshm_unlock *
130 * *
131 ******************************************************************************/
zbx_dshm_unlock(zbx_dshm_t * shm)132 void zbx_dshm_unlock(zbx_dshm_t *shm)
133 {
134 zbx_mutex_unlock(shm->lock);
135 }
136
137 /******************************************************************************
138 * *
139 * Function: zbx_dshm_validate_ref *
140 * *
141 * Purpose: validates local reference to dynamic shared memory segment *
142 * *
143 * Parameters: shm - [IN] the dynamic shared memory data *
144 * shm_ref - [IN/OUT] a local reference to dynamic shared memory *
145 * segment *
146 * errmsg - [OUT] the error message *
147 * *
148 * Return value: SUCCEED - the local reference to dynamic shared memory *
149 * segment was validated successfully and contains *
150 * correct dynamic shared memory segment address *
151 * FAIL - otherwise. The errmsg contains error message and *
152 * must be freed by the caller. *
153 * *
154 * Comments: This function should be called before accessing the dynamic *
155 * shared memory to ensure that the local reference has correct *
156 * address after shared memory allocation/reallocation. *
157 * *
158 ******************************************************************************/
zbx_dshm_validate_ref(const zbx_dshm_t * shm,zbx_dshm_ref_t * shm_ref,char ** errmsg)159 int zbx_dshm_validate_ref(const zbx_dshm_t *shm, zbx_dshm_ref_t *shm_ref, char **errmsg)
160 {
161 int ret = FAIL;
162
163 zabbix_log(LOG_LEVEL_TRACE, "In %s() shmid:%d refid:%d", __func__, shm->shmid, shm_ref->shmid);
164
165 if (shm->shmid != shm_ref->shmid)
166 {
167 if (ZBX_NONEXISTENT_SHMID != shm_ref->shmid)
168 {
169 if (-1 == shmdt((void *)shm_ref->addr))
170 {
171 *errmsg = zbx_dsprintf(*errmsg, "cannot detach shared memory: %s", zbx_strerror(errno));
172 goto out;
173 }
174 shm_ref->addr = NULL;
175 shm_ref->shmid = ZBX_NONEXISTENT_SHMID;
176 }
177
178 if ((void *)(-1) == (shm_ref->addr = shmat(shm->shmid, NULL, 0)))
179 {
180 *errmsg = zbx_dsprintf(*errmsg, "cannot attach shared memory: %s", zbx_strerror(errno));
181 shm_ref->addr = NULL;
182 goto out;
183 }
184
185 shm_ref->shmid = shm->shmid;
186 }
187
188 ret = SUCCEED;
189 out:
190 zabbix_log(LOG_LEVEL_TRACE, "End of %s():%s", __func__, zbx_result_string(ret));
191
192 return ret;
193 }
194
195 /******************************************************************************
196 * *
197 * Function: zbx_dshm_realloc *
198 * *
199 * Purpose: reallocates dynamic shared memory segment *
200 * *
201 * Parameters: shm - [IN/OUT] the dynamic shared memory data *
202 * size - [IN] the new segment size *
203 * errmsg - [OUT] the error message *
204 * *
205 * Return value: *
206 * SUCCEED - the shared memory segment was successfully reallocated. *
207 * FAIL - otherwise. The errmsg contains error message and must be *
208 * freed by the caller. *
209 * *
210 * Comments: The shared memory segment is reallocated by simply creating *
211 * a new segment and copying the data from old segment by calling *
212 * the copy_data callback function. *
213 * *
214 ******************************************************************************/
zbx_dshm_realloc(zbx_dshm_t * shm,size_t size,char ** errmsg)215 int zbx_dshm_realloc(zbx_dshm_t *shm, size_t size, char **errmsg)
216 {
217 int shmid, ret = FAIL;
218 void *addr, *addr_old = NULL;
219 size_t shm_size;
220
221 zabbix_log(LOG_LEVEL_DEBUG, "In %s() shmid:%d size:" ZBX_FS_SIZE_T, __func__, shm->shmid, (zbx_fs_size_t)size);
222
223 shm_size = ZBX_SIZE_T_ALIGN8(size);
224
225 /* attach to the old segment if possible */
226 if (ZBX_NONEXISTENT_SHMID != shm->shmid && (void *)(-1) == (addr_old = shmat(shm->shmid, NULL, 0)))
227 {
228 *errmsg = zbx_dsprintf(*errmsg, "cannot attach current shared memory: %s", zbx_strerror(errno));
229 goto out;
230 }
231
232 if (-1 == (shmid = zbx_shm_create(shm_size)))
233 {
234 *errmsg = zbx_strdup(NULL, "cannot allocate shared memory");
235 goto out;
236 }
237
238 if ((void *)(-1) == (addr = shmat(shmid, NULL, 0)))
239 {
240 if (NULL != addr_old)
241 (void)shmdt(addr_old);
242
243 *errmsg = zbx_dsprintf(*errmsg, "cannot attach new shared memory: %s", zbx_strerror(errno));
244 goto out;
245 }
246
247 /* copy data from the old segment */
248 shm->copy_func(addr, shm_size, addr_old);
249
250 if (-1 == shmdt((void *)addr))
251 {
252 *errmsg = zbx_strdup(*errmsg, "cannot detach from new shared memory");
253 goto out;
254 }
255
256 /* delete the old segment */
257 if (NULL != addr_old && -1 == zbx_shm_destroy(shm->shmid))
258 {
259 *errmsg = zbx_strdup(*errmsg, "cannot detach from old shared memory");
260 goto out;
261 }
262
263 shm->size = shm_size;
264 shm->shmid = shmid;
265
266 ret = SUCCEED;
267 out:
268 zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s shmid:%d", __func__, zbx_result_string(ret), shm->shmid);
269
270 return ret;
271 }
272