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 "module.h"
22 #include "zbxmodules.h"
23 
24 #include "log.h"
25 #include "sysinfo.h"
26 
27 #define ZBX_MODULE_FUNC_INIT		"zbx_module_init"
28 #define ZBX_MODULE_FUNC_API_VERSION	"zbx_module_api_version"
29 #define ZBX_MODULE_FUNC_ITEM_LIST	"zbx_module_item_list"
30 #define ZBX_MODULE_FUNC_ITEM_PROCESS	"zbx_module_item_process"
31 #define ZBX_MODULE_FUNC_ITEM_TIMEOUT	"zbx_module_item_timeout"
32 #define ZBX_MODULE_FUNC_UNINIT		"zbx_module_uninit"
33 
34 static void	**modules = NULL;
35 
36 /******************************************************************************
37  *                                                                            *
38  * Function: register_module                                                  *
39  *                                                                            *
40  * Purpose: Add module to the list of loaded modules (dynamic libraries).     *
41  *          It skips a module if it is already registered.                    *
42  *                                                                            *
43  * Parameters: module - library handler                                       *
44  *                                                                            *
45  * Return value: SUCCEED - if module is successfully registered               *
46  *               FAIL - if module is already registered                       *
47  *                                                                            *
48  ******************************************************************************/
register_module(void * module)49 static int	register_module(void *module)
50 {
51 	const char	*__function_name = "register_module";
52 
53 	int		i = 0, ret = FAIL;
54 
55 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
56 
57 	if (NULL == modules)
58 	{
59 		modules = zbx_malloc(modules, sizeof(void *));
60 		modules[0] = NULL;
61 	}
62 
63 	while (NULL != modules[i])
64 	{
65 		if (module == modules[i])	/* a module is already registered */
66 			goto out;
67 		i++;
68 	}
69 
70 	modules = zbx_realloc(modules, (i + 2) * sizeof(void *));
71 	modules[i] = module;
72 	modules[i + 1] = NULL;
73 
74 	ret = SUCCEED;
75 out:
76 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
77 
78 	return ret;
79 }
80 
81 /******************************************************************************
82  *                                                                            *
83  * Function: load_modules                                                     *
84  *                                                                            *
85  * Purpose: load loadable modules (dynamic libraries)                         *
86  *          It skips a module in case of any errors                           *
87  *                                                                            *
88  * Parameters: path - directory where modules are located                     *
89  *             file_names - list of module names                              *
90  *             timeout - timeout in seconds for processing of items by module *
91  *             verbose - output list of loaded modules                        *
92  *                                                                            *
93  * Return value: SUCCEED - all modules is successfully loaded                 *
94  *               FAIL - loading of modules failed                             *
95  *                                                                            *
96  ******************************************************************************/
load_modules(const char * path,char ** file_names,int timeout,int verbose)97 int	load_modules(const char *path, char **file_names, int timeout, int verbose)
98 {
99 	const char	*__function_name = "load_modules";
100 
101 	char		**file_name, *buffer = NULL;
102 	void		*lib;
103 	char		full_name[MAX_STRING_LEN], error[MAX_STRING_LEN];
104 	int		(*func_init)(), (*func_version)();
105 	ZBX_METRIC	*(*func_list)();
106 	void		(*func_timeout)();
107 	int		i, ret = FAIL;
108 
109 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
110 
111 	for (file_name = file_names; NULL != *file_name; file_name++)
112 	{
113 		zbx_snprintf(full_name, sizeof(full_name), "%s/%s", path, *file_name);
114 
115 		zabbix_log(LOG_LEVEL_DEBUG, "loading module \"%s\"", full_name);
116 
117 		if (NULL == (lib = dlopen(full_name, RTLD_NOW)))
118 		{
119 			zabbix_log(LOG_LEVEL_CRIT, "cannot load module \"%s\": %s", *file_name, dlerror());
120 			goto fail;
121 		}
122 
123 		func_version = (int (*)(void))dlsym(lib, ZBX_MODULE_FUNC_API_VERSION);
124 		if (NULL == func_version)
125 		{
126 			zabbix_log(LOG_LEVEL_CRIT, "cannot find \"" ZBX_MODULE_FUNC_API_VERSION "()\""
127 					" function in module \"%s\": %s", *file_name, dlerror());
128 			dlclose(lib);
129 			goto fail;
130 		}
131 
132 		if (ZBX_MODULE_API_VERSION_ONE != (i = func_version()))
133 		{
134 			zabbix_log(LOG_LEVEL_CRIT, "unsupported module \"%s\" version: %d", *file_name, i);
135 			dlclose(lib);
136 			goto fail;
137 		}
138 
139 		func_init = (int (*)(void))dlsym(lib, ZBX_MODULE_FUNC_INIT);
140 		if (NULL == func_init)
141 		{
142 			zabbix_log(LOG_LEVEL_CRIT, "cannot find \"" ZBX_MODULE_FUNC_INIT "()\""
143 					" function in module \"%s\": %s", *file_name, dlerror());
144 			dlclose(lib);
145 			goto fail;
146 		}
147 
148 		if (ZBX_MODULE_OK != func_init())
149 		{
150 			zabbix_log(LOG_LEVEL_CRIT, "cannot initialize module \"%s\"", *file_name);
151 			dlclose(lib);
152 			goto fail;
153 		}
154 
155 		/* the function is optional, zabbix will load the module even if it is missing */
156 		func_timeout = (void (*)(void))dlsym(lib, ZBX_MODULE_FUNC_ITEM_TIMEOUT);
157 		if (NULL == func_timeout)
158 		{
159 			zabbix_log(LOG_LEVEL_DEBUG, "cannot find \"" ZBX_MODULE_FUNC_ITEM_TIMEOUT "()\""
160 					" function in module \"%s\": %s", *file_name, dlerror());
161 		}
162 		else
163 			func_timeout(timeout);
164 
165 		func_list = (ZBX_METRIC *(*)(void))dlsym(lib, ZBX_MODULE_FUNC_ITEM_LIST);
166 		if (NULL == func_list)
167 		{
168 			zabbix_log(LOG_LEVEL_WARNING, "cannot find \"" ZBX_MODULE_FUNC_ITEM_LIST "()\""
169 					" function in module \"%s\": %s", *file_name, dlerror());
170 			dlclose(lib);
171 			continue;
172 		}
173 
174 		if (SUCCEED == register_module(lib))
175 		{
176 			ZBX_METRIC	*metrics;
177 
178 			metrics = func_list();
179 
180 			for (i = 0; NULL != metrics[i].key; i++)
181 			{
182 				/* accept only CF_HAVEPARAMS flag from module items */
183 				metrics[i].flags &= CF_HAVEPARAMS;
184 				/* the flag means that the items comes from a loadable module */
185 				metrics[i].flags |= CF_MODULE;
186 				if (SUCCEED != add_metric(&metrics[i], error, sizeof(error)))
187 				{
188 					zabbix_log(LOG_LEVEL_CRIT, "cannot load module \"%s\": %s", *file_name, error);
189 					exit(EXIT_FAILURE);
190 				}
191 			}
192 
193 			if (1 == verbose)
194 			{
195 				if (NULL != buffer)
196 					buffer = zbx_strdcat(buffer, ", ");
197 				buffer = zbx_strdcat(buffer, *file_name);
198 			}
199 		}
200 	}
201 
202 	if (NULL != buffer)
203 		zabbix_log(LOG_LEVEL_WARNING, "loaded modules: %s", buffer);
204 
205 	ret = SUCCEED;
206 fail:
207 	zbx_free(buffer);
208 
209 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s():%s", __function_name, zbx_result_string(ret));
210 
211 	return ret;
212 }
213 
214 /******************************************************************************
215  *                                                                            *
216  * Function: unload_modules                                                   *
217  *                                                                            *
218  * Purpose: Unload already loaded loadable modules (dynamic libraries).       *
219  *          It is called on process shutdown.                                 *
220  *                                                                            *
221  ******************************************************************************/
unload_modules()222 void	unload_modules()
223 {
224 	const char	*__function_name = "unload_modules";
225 
226 	int		(*func_uninit)();
227 	void		**module;
228 
229 	zabbix_log(LOG_LEVEL_DEBUG, "In %s()", __function_name);
230 
231 	/* there are no registered modules */
232 	if (NULL == modules)
233 		goto out;
234 
235 	for (module = modules; NULL != *module; module++)
236 	{
237 		func_uninit = (int (*)(void))dlsym(*module, ZBX_MODULE_FUNC_UNINIT);
238 		if (NULL == func_uninit)
239 		{
240 			zabbix_log(LOG_LEVEL_DEBUG, "cannot find \"" ZBX_MODULE_FUNC_UNINIT "()\" function: %s",
241 					dlerror());
242 			dlclose(*module);
243 			continue;
244 		}
245 
246 		if (ZBX_MODULE_OK != func_uninit())
247 			zabbix_log(LOG_LEVEL_WARNING, "uninitialization failed");
248 
249 		dlclose(*module);
250 	}
251 
252 	zbx_free(modules);
253 out:
254 	zabbix_log(LOG_LEVEL_DEBUG, "End of %s()", __function_name);
255 }
256