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