1 /** 2 * Copyright © 2016 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include "config.h" 17 #include <stdlib.h> 18 #include <string.h> 19 #include <stdio.h> 20 #include <errno.h> 21 #include <unistd.h> 22 #include <sys/timerfd.h> 23 #include <systemd/sd-bus.h> 24 #include <systemd/sd-event.h> 25 #include "mapper.h" 26 27 static const char *async_wait_introspection_match = 28 "type='signal'," 29 "sender='xyz.openbmc_project.ObjectMapper'," 30 "interface='xyz.openbmc_project.ObjectMapper.Private'," 31 "member='IntrospectionComplete'"; 32 33 static const char *async_wait_interfaces_added_match = 34 "type='signal'," 35 "interface='org.freedesktop.DBus.ObjectManager'," 36 "member='InterfacesAdded'"; 37 38 static const int mapper_busy_retries = 5; 39 static const uint64_t mapper_busy_delay_interval_usec = 1000000; 40 41 struct mapper_async_wait 42 { 43 char **objs; 44 void (*callback)(int, void *); 45 void *userdata; 46 sd_event *loop; 47 sd_bus *conn; 48 sd_bus_slot *introspection_slot; 49 sd_bus_slot *intf_slot; 50 int *status; 51 int count; 52 int finished; 53 int r; 54 }; 55 56 struct async_wait_callback_data 57 { 58 mapper_async_wait *wait; 59 const char *path; 60 sd_event_source *event_source; 61 int retry; 62 }; 63 64 static int async_wait_match_introspection_complete(sd_bus_message *, void *, 65 sd_bus_error *); 66 static int async_wait_check_done(mapper_async_wait *); 67 static void async_wait_done(int r, mapper_async_wait *); 68 static int async_wait_get_objects(mapper_async_wait *); 69 static int async_wait_getobject_callback(sd_bus_message *, 70 void *, sd_bus_error *); 71 72 static int sarraylen(char *array[]) 73 { 74 int count = 0; 75 char **p = array; 76 77 while(*p != NULL) { 78 ++count; 79 ++p; 80 } 81 82 return count; 83 } 84 85 static void sarrayfree(char *array[]) 86 { 87 char **p = array; 88 while(*p != NULL) { 89 free(*p); 90 ++p; 91 } 92 free(array); 93 } 94 95 static char **sarraydup(char *array[]) 96 { 97 int count = sarraylen(array); 98 int i; 99 char **ret = NULL; 100 101 ret = malloc(sizeof(*ret) * count); 102 if(!ret) 103 return NULL; 104 105 for(i=0; i<count; ++i) { 106 ret[i] = strdup(array[i]); 107 if(!ret[i]) 108 goto error; 109 } 110 111 return ret; 112 113 error: 114 sarrayfree(ret); 115 return NULL; 116 } 117 118 static int async_wait_timeout_callback(sd_event_source *s, 119 uint64_t usec, void *userdata) 120 { 121 int r; 122 struct async_wait_callback_data *data = userdata; 123 mapper_async_wait *wait = data->wait; 124 125 sd_event_source_unref(data->event_source); 126 r = sd_bus_call_method_async( 127 wait->conn, 128 NULL, 129 MAPPER_BUSNAME, 130 MAPPER_PATH, 131 MAPPER_INTERFACE, 132 "GetObject", 133 async_wait_getobject_callback, 134 data, 135 "sas", 136 data->path, 137 0, 138 NULL); 139 if(r < 0) { 140 async_wait_done(r, wait); 141 free(data); 142 } 143 144 return 0; 145 } 146 147 static int async_wait_getobject_callback(sd_bus_message *m, 148 void *userdata, 149 sd_bus_error *e) 150 { 151 int i, r; 152 struct async_wait_callback_data *data = userdata; 153 mapper_async_wait *wait = data->wait; 154 uint64_t now; 155 156 if(wait->finished) 157 goto exit; 158 159 r = sd_bus_message_get_errno(m); 160 if(r == ENOENT) 161 goto exit; 162 163 if(r == EBUSY && data->retry < mapper_busy_retries) { 164 r = sd_event_now(wait->loop, 165 CLOCK_MONOTONIC, 166 &now); 167 if(r < 0) { 168 async_wait_done(r, wait); 169 goto exit; 170 } 171 172 ++data->retry; 173 r = sd_event_add_time(wait->loop, 174 &data->event_source, 175 CLOCK_MONOTONIC, 176 now + mapper_busy_delay_interval_usec, 177 0, 178 async_wait_timeout_callback, 179 data); 180 if(r < 0) { 181 async_wait_done(r, wait); 182 goto exit; 183 } 184 185 return 0; 186 } 187 188 if(r) { 189 async_wait_done(-r, wait); 190 goto exit; 191 } 192 193 for(i=0; i<wait->count; ++i) { 194 if(!strcmp(data->path, wait->objs[i])) { 195 wait->status[i] = 1; 196 } 197 } 198 199 if(async_wait_check_done(wait)) 200 async_wait_done(0, wait); 201 202 exit: 203 free(data); 204 return 0; 205 } 206 207 static int async_wait_get_objects(mapper_async_wait *wait) 208 { 209 int i, r; 210 struct async_wait_callback_data *data = NULL; 211 212 for(i=0; i<wait->count; ++i) { 213 if(wait->status[i]) 214 continue; 215 data = malloc(sizeof(*data)); 216 data->wait = wait; 217 data->path = wait->objs[i]; 218 data->retry = 0; 219 data->event_source = NULL; 220 r = sd_bus_call_method_async( 221 wait->conn, 222 NULL, 223 MAPPER_BUSNAME, 224 MAPPER_PATH, 225 MAPPER_INTERFACE, 226 "GetObject", 227 async_wait_getobject_callback, 228 data, 229 "sas", 230 wait->objs[i], 231 0, 232 NULL); 233 if(r < 0) { 234 free(data); 235 fprintf(stderr, "Error invoking method: %s\n", 236 strerror(-r)); 237 return r; 238 } 239 } 240 241 return 0; 242 } 243 244 static int async_wait_match_introspection_complete(sd_bus_message *m, void *w, 245 sd_bus_error *e) 246 { 247 int r; 248 249 mapper_async_wait *wait = w; 250 if(wait->finished) 251 return 0; 252 253 r = async_wait_get_objects(wait); 254 if(r < 0) 255 async_wait_done(r, wait); 256 257 return 0; 258 } 259 260 static void async_wait_done(int r, mapper_async_wait *w) 261 { 262 if(w->finished) 263 return; 264 265 w->finished = 1; 266 sd_bus_slot_unref(w->introspection_slot); 267 sd_bus_slot_unref(w->intf_slot); 268 269 if(w->callback) 270 w->callback(r, w->userdata); 271 } 272 273 static int async_wait_check_done(mapper_async_wait *w) 274 { 275 int i; 276 277 if(w->finished) 278 return 1; 279 280 for(i=0; i<w->count; ++i) 281 if(!w->status[i]) 282 return 0; 283 284 return 1; 285 } 286 287 void mapper_wait_async_free(mapper_async_wait *w) 288 { 289 free(w->status); 290 sarrayfree(w->objs); 291 free(w); 292 } 293 294 int mapper_wait_async(sd_bus *conn, 295 sd_event *loop, 296 char *objs[], 297 void (*callback)(int, void *), 298 void *userdata, 299 mapper_async_wait **w) 300 { 301 int r; 302 mapper_async_wait *wait = NULL; 303 304 wait = malloc(sizeof(*wait)); 305 if(!wait) 306 return -ENOMEM; 307 308 memset(wait, 0, sizeof(*wait)); 309 wait->conn = conn; 310 wait->loop = loop; 311 wait->callback = callback; 312 wait->userdata = userdata; 313 wait->count = sarraylen(objs); 314 if(!wait->count) 315 return 0; 316 317 wait->objs = sarraydup(objs); 318 if(!wait->objs) { 319 r = -ENOMEM; 320 goto free_wait; 321 } 322 323 wait->status = malloc(sizeof(*wait->status) * wait->count); 324 if(!wait->status) { 325 r = -ENOMEM; 326 goto free_objs; 327 } 328 memset(wait->status, 0, sizeof(*wait->status) * wait->count); 329 330 r = sd_bus_add_match(conn, 331 &wait->introspection_slot, 332 async_wait_introspection_match, 333 async_wait_match_introspection_complete, 334 wait); 335 if(r < 0) { 336 fprintf(stderr, "Error adding match rule: %s\n", 337 strerror(-r)); 338 goto free_status; 339 } 340 341 r = sd_bus_add_match(conn, 342 &wait->intf_slot, 343 async_wait_interfaces_added_match, 344 async_wait_match_introspection_complete, 345 wait); 346 if(r < 0) { 347 fprintf(stderr, "Error adding match rule: %s\n", 348 strerror(-r)); 349 goto unref_name_slot; 350 } 351 352 r = async_wait_get_objects(wait); 353 if(r < 0) { 354 fprintf(stderr, "Error calling method: %s\n", 355 strerror(-r)); 356 goto unref_intf_slot; 357 } 358 359 *w = wait; 360 361 return 0; 362 363 unref_intf_slot: 364 sd_bus_slot_unref(wait->intf_slot); 365 unref_name_slot: 366 sd_bus_slot_unref(wait->introspection_slot); 367 free_status: 368 free(wait->status); 369 free_objs: 370 sarrayfree(wait->objs); 371 free_wait: 372 free(wait); 373 374 return r; 375 } 376 377 int mapper_get_object(sd_bus *conn, const char *obj, sd_bus_message **reply) 378 { 379 sd_bus_error error = SD_BUS_ERROR_NULL; 380 sd_bus_message *request = NULL; 381 int r, retry = 0; 382 383 r = sd_bus_message_new_method_call( 384 conn, 385 &request, 386 MAPPER_BUSNAME, 387 MAPPER_PATH, 388 MAPPER_INTERFACE, 389 "GetObject"); 390 if (r < 0) 391 goto exit; 392 393 r = sd_bus_message_append(request, "s", obj); 394 if (r < 0) 395 goto exit; 396 r = sd_bus_message_append(request, "as", 0, NULL); 397 if (r < 0) 398 goto exit; 399 400 while(retry < mapper_busy_retries) { 401 sd_bus_error_free(&error); 402 r = sd_bus_call(conn, request, 0, &error, reply); 403 if (r < 0 && sd_bus_error_get_errno(&error) == EBUSY) { 404 ++retry; 405 406 if(retry != mapper_busy_retries) 407 usleep(mapper_busy_delay_interval_usec); 408 continue; 409 } 410 break; 411 } 412 413 if (r < 0) 414 goto exit; 415 416 exit: 417 sd_bus_error_free(&error); 418 sd_bus_message_unref(request); 419 420 return r; 421 } 422 423 int mapper_get_service(sd_bus *conn, const char *obj, char **service) 424 { 425 sd_bus_message *reply = NULL; 426 const char *tmp; 427 int r; 428 429 r = mapper_get_object(conn, obj, &reply); 430 if (r < 0) 431 goto exit; 432 433 r = sd_bus_message_enter_container(reply, 0, NULL); 434 if (r < 0) 435 goto exit; 436 437 r = sd_bus_message_enter_container(reply, 0, NULL); 438 if (r < 0) 439 goto exit; 440 441 r = sd_bus_message_read(reply, "s", &tmp); 442 if (r < 0) 443 goto exit; 444 445 *service = strdup(tmp); 446 447 exit: 448 sd_bus_message_unref(reply); 449 450 return r; 451 } 452