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