2 * Copyright (C) 2015 - 2016 Red Hat, Inc.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 * Author: Gris Ge <fge@redhat.com>
18 * Todd Gill <tgill@redhat.com>
24 #include <sys/resource.h>
27 #include <libdevmapper.h>
32 #include <mpath_cmd.h>
34 #include "libdmmp/libdmmp.h"
35 #include "libdmmp_private.h"
37 #define _DEFAULT_UXSOCK_TIMEOUT 60000
38 /* ^ 60 seconds. On system with 10k sdX, dmmp_mpath_array_get()
39 * only take 3.5 seconds, so this default value should be OK for most users.
42 #define _DMMP_IPC_SHOW_JSON_CMD "show maps json"
43 #define _DMMP_JSON_MAJOR_KEY "major_version"
44 #define _DMMP_JSON_MAJOR_VERSION 0
45 #define _DMMP_JSON_MAPS_KEY "maps"
46 #define _ERRNO_STR_BUFF_SIZE 256
49 void (*log_func)(struct dmmp_context *ctx, int priority,
50 const char *file, int line, const char *func_name,
51 const char *format, va_list args);
57 _dmmp_getter_func_gen(dmmp_context_log_priority_get,
58 struct dmmp_context, ctx, log_priority,
61 _dmmp_getter_func_gen(dmmp_context_userdata_get, struct dmmp_context, ctx,
64 _dmmp_getter_func_gen(dmmp_context_timeout_get, struct dmmp_context, ctx, tmo,
67 _dmmp_array_free_func_gen(dmmp_mpath_array_free, struct dmmp_mpath,
70 void _dmmp_log(struct dmmp_context *ctx, int priority, const char *file,
71 int line, const char *func_name, const char *format, ...)
75 va_start(args, format);
76 ctx->log_func(ctx, priority, file, line, func_name, format, args);
80 struct dmmp_context *dmmp_context_new(void)
82 struct dmmp_context *ctx = NULL;
84 ctx = (struct dmmp_context *) malloc(sizeof(struct dmmp_context));
89 ctx->log_func = _dmmp_log_stderr;
90 ctx->log_priority = DMMP_LOG_PRIORITY_DEFAULT;
92 ctx->tmo = _DEFAULT_UXSOCK_TIMEOUT;
97 void dmmp_context_free(struct dmmp_context *ctx)
102 void dmmp_context_log_priority_set(struct dmmp_context *ctx, int priority)
105 ctx->log_priority = priority;
108 void dmmp_context_timeout_set(struct dmmp_context *ctx, unsigned int tmo)
114 void dmmp_context_log_func_set
115 (struct dmmp_context *ctx,
116 void (*log_func)(struct dmmp_context *ctx, int priority,
117 const char *file, int line, const char *func_name,
118 const char *format, va_list args))
121 ctx->log_func = log_func;
124 void dmmp_context_userdata_set(struct dmmp_context *ctx, void *userdata)
127 ctx->userdata = userdata;
130 int dmmp_mpath_array_get(struct dmmp_context *ctx,
131 struct dmmp_mpath ***dmmp_mps, uint32_t *dmmp_mp_count)
133 struct dmmp_mpath *dmmp_mp = NULL;
136 json_object *j_obj = NULL;
137 json_object *j_obj_map = NULL;
138 enum json_tokener_error j_err = json_tokener_success;
139 json_tokener *j_token = NULL;
140 struct array_list *ar_maps = NULL;
142 int cur_json_major_version = -1;
143 int ar_maps_len = -1;
146 char errno_str_buff[_ERRNO_STR_BUFF_SIZE];
149 assert(dmmp_mps != NULL);
150 assert(dmmp_mp_count != NULL);
155 socket_fd = mpath_connect();
156 if (socket_fd == -1) {
158 memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
159 strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
160 if (errno_save == ECONNREFUSED) {
161 rc = DMMP_ERR_NO_DAEMON;
162 _error(ctx, "Socket connection refuse. "
163 "Maybe multipathd daemon is not running");
165 _error(ctx, "IPC failed with error %d(%s)", errno_save,
167 rc = DMMP_ERR_IPC_ERROR;
172 if (mpath_process_cmd(socket_fd, _DMMP_IPC_SHOW_JSON_CMD,
173 &j_str, ctx->tmo) != 0) {
175 memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
176 strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
177 if (errno_save == ETIMEDOUT) {
178 rc = DMMP_ERR_IPC_TIMEOUT;
179 _error(ctx, "IPC communication timeout, try to "
180 "increase it via dmmp_context_timeout_set()");
183 _error(ctx, "IPC failed when process command '%s' with "
184 "error %d(%s)", _DMMP_IPC_SHOW_JSON_CMD, errno_save,
186 rc = DMMP_ERR_IPC_ERROR;
190 if ((j_str == NULL) || (strlen(j_str) == 0)) {
191 _error(ctx, "IPC return empty reply for command %s",
192 _DMMP_IPC_SHOW_JSON_CMD);
193 rc = DMMP_ERR_IPC_ERROR;
197 _debug(ctx, "Got json output from multipathd: '%s'", j_str);
198 j_token = json_tokener_new();
199 if (j_token == NULL) {
201 _error(ctx, "BUG: json_tokener_new() retuned NULL");
204 j_obj = json_tokener_parse_ex(j_token, j_str, strlen(j_str) + 1);
207 rc = DMMP_ERR_IPC_ERROR;
208 j_err = json_tokener_get_error(j_token);
209 _error(ctx, "Failed to parse JSON output from multipathd IPC: "
210 "%s", json_tokener_error_desc(j_err));
214 _json_obj_get_value(ctx, j_obj, cur_json_major_version,
215 _DMMP_JSON_MAJOR_KEY, json_type_int,
216 json_object_get_int, rc, out);
218 if (cur_json_major_version != _DMMP_JSON_MAJOR_VERSION) {
219 rc = DMMP_ERR_INCOMPATIBLE;
220 _error(ctx, "Incompatible multipathd JSON major version %d, "
221 "should be %d", cur_json_major_version,
222 _DMMP_JSON_MAJOR_VERSION);
225 _debug(ctx, "multipathd JSON major version(%d) check pass",
226 _DMMP_JSON_MAJOR_VERSION);
228 _json_obj_get_value(ctx, j_obj, ar_maps, _DMMP_JSON_MAPS_KEY,
229 json_type_array, json_object_get_array, rc, out);
231 if (ar_maps == NULL) {
233 _error(ctx, "BUG: Got NULL map array from "
234 "_json_obj_get_value()");
238 ar_maps_len = array_list_length(ar_maps);
239 if (ar_maps_len < 0) {
241 _error(ctx, "BUG: Got negative length for ar_maps");
244 else if (ar_maps_len == 0)
247 *dmmp_mp_count = ar_maps_len & UINT32_MAX;
249 *dmmp_mps = (struct dmmp_mpath **)
250 malloc(sizeof(struct dmmp_mpath *) * (*dmmp_mp_count));
251 _dmmp_alloc_null_check(ctx, dmmp_mps, rc, out);
252 for (; i < *dmmp_mp_count; ++i)
253 (*dmmp_mps)[i] = NULL;
255 for (i = 0; i < *dmmp_mp_count; ++i) {
256 j_obj_map = array_list_get_idx(ar_maps, i);
257 if (j_obj_map == NULL) {
259 _error(ctx, "BUG: array_list_get_idx() return NULL");
263 dmmp_mp = _dmmp_mpath_new();
264 _dmmp_alloc_null_check(ctx, dmmp_mp, rc, out);
265 (*dmmp_mps)[i] = dmmp_mp;
266 _good(_dmmp_mpath_update(ctx, dmmp_mp, j_obj_map), rc, out);
271 mpath_disconnect(socket_fd);
274 json_tokener_free(j_token);
276 json_object_put(j_obj);
279 dmmp_mpath_array_free(*dmmp_mps, *dmmp_mp_count);