agent.c revision 9184e2eeb7b97371c6b83b747c8984e2340d2b47
1/* 2 * 3 * BlueZ - Bluetooth protocol stack for Linux 4 * 5 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> 6 * 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 21 * 22 */ 23 24#ifdef HAVE_CONFIG_H 25#include <config.h> 26#endif 27 28#include <stdio.h> 29#include <errno.h> 30#include <unistd.h> 31#include <stdlib.h> 32#include <signal.h> 33#include <getopt.h> 34#include <string.h> 35 36#include <dbus/dbus.h> 37 38static char *passkey_value = NULL; 39static int passkey_delay = 0; 40static int do_reject = 0; 41 42static volatile sig_atomic_t __io_canceled = 0; 43static volatile sig_atomic_t __io_terminated = 0; 44 45static void sig_term(int sig) 46{ 47 __io_canceled = 1; 48} 49 50static DBusHandlerResult agent_filter(DBusConnection *conn, 51 DBusMessage *msg, void *data) 52{ 53 const char *name, *old, *new; 54 55 if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, 56 "NameOwnerChanged")) 57 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 58 59 if (!dbus_message_get_args(msg, NULL, 60 DBUS_TYPE_STRING, &name, 61 DBUS_TYPE_STRING, &old, 62 DBUS_TYPE_STRING, &new, 63 DBUS_TYPE_INVALID)) { 64 fprintf(stderr, "Invalid arguments for NameOwnerChanged signal"); 65 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 66 } 67 68 if (!strcmp(name, "org.bluez") && *new == '\0') { 69 fprintf(stderr, "Agent has been terminated\n"); 70 __io_terminated = 1; 71 } 72 73 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 74} 75 76static DBusHandlerResult request_pincode_message(DBusConnection *conn, 77 DBusMessage *msg, void *data) 78{ 79 DBusMessage *reply; 80 const char *path; 81 82 if (!passkey_value) 83 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 84 85 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, 86 DBUS_TYPE_INVALID)) { 87 fprintf(stderr, "Invalid arguments for RequestPinCode method"); 88 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 89 } 90 91 if (do_reject) { 92 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", ""); 93 goto send; 94 } 95 96 reply = dbus_message_new_method_return(msg); 97 if (!reply) { 98 fprintf(stderr, "Can't create reply message\n"); 99 return DBUS_HANDLER_RESULT_NEED_MEMORY; 100 } 101 102 printf("Pincode request for device %s\n", path); 103 104 if (passkey_delay) { 105 printf("Waiting for %d seconds\n", passkey_delay); 106 sleep(passkey_delay); 107 } 108 109 dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey_value, 110 DBUS_TYPE_INVALID); 111 112send: 113 dbus_connection_send(conn, reply, NULL); 114 115 dbus_connection_flush(conn); 116 117 dbus_message_unref(reply); 118 119 return DBUS_HANDLER_RESULT_HANDLED; 120} 121 122static DBusHandlerResult request_passkey_message(DBusConnection *conn, 123 DBusMessage *msg, void *data) 124{ 125 DBusMessage *reply; 126 const char *path; 127 unsigned int passkey; 128 129 if (!passkey_value) 130 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 131 132 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, 133 DBUS_TYPE_INVALID)) { 134 fprintf(stderr, "Invalid arguments for RequestPasskey method"); 135 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 136 } 137 138 if (do_reject) { 139 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", ""); 140 goto send; 141 } 142 143 reply = dbus_message_new_method_return(msg); 144 if (!reply) { 145 fprintf(stderr, "Can't create reply message\n"); 146 return DBUS_HANDLER_RESULT_NEED_MEMORY; 147 } 148 149 printf("Passkey request for device %s\n", path); 150 151 if (passkey_delay) { 152 printf("Waiting for %d seconds\n", passkey_delay); 153 sleep(passkey_delay); 154 } 155 156 passkey = strtoul(passkey_value, NULL, 10); 157 158 dbus_message_append_args(reply, DBUS_TYPE_UINT32, &passkey, 159 DBUS_TYPE_INVALID); 160 161send: 162 dbus_connection_send(conn, reply, NULL); 163 164 dbus_connection_flush(conn); 165 166 dbus_message_unref(reply); 167 168 return DBUS_HANDLER_RESULT_HANDLED; 169} 170 171static DBusHandlerResult request_confirmation_message(DBusConnection *conn, 172 DBusMessage *msg, void *data) 173{ 174 DBusMessage *reply; 175 const char *path; 176 unsigned int passkey; 177 178 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, 179 DBUS_TYPE_UINT32, &passkey, 180 DBUS_TYPE_INVALID)) { 181 fprintf(stderr, "Invalid arguments for RequestPasskey method"); 182 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 183 } 184 185 if (do_reject) { 186 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", ""); 187 goto send; 188 } 189 190 reply = dbus_message_new_method_return(msg); 191 if (!reply) { 192 fprintf(stderr, "Can't create reply message\n"); 193 return DBUS_HANDLER_RESULT_NEED_MEMORY; 194 } 195 196 printf("Confirmation request of %u for device %s\n", passkey, path); 197 198 if (passkey_delay) { 199 printf("Waiting for %d seconds\n", passkey_delay); 200 sleep(passkey_delay); 201 } 202 203send: 204 dbus_connection_send(conn, reply, NULL); 205 206 dbus_connection_flush(conn); 207 208 dbus_message_unref(reply); 209 210 return DBUS_HANDLER_RESULT_HANDLED; 211} 212 213static DBusHandlerResult authorize_message(DBusConnection *conn, 214 DBusMessage *msg, void *data) 215{ 216 DBusMessage *reply; 217 const char *path, *uuid; 218 219 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, 220 DBUS_TYPE_STRING, &uuid, 221 DBUS_TYPE_INVALID)) { 222 fprintf(stderr, "Invalid arguments for Authorize method"); 223 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 224 } 225 226 if (do_reject) { 227 reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", ""); 228 goto send; 229 } 230 231 reply = dbus_message_new_method_return(msg); 232 if (!reply) { 233 fprintf(stderr, "Can't create reply message\n"); 234 return DBUS_HANDLER_RESULT_NEED_MEMORY; 235 } 236 237 printf("Authorizing request for %s\n", path); 238 239send: 240 dbus_connection_send(conn, reply, NULL); 241 242 dbus_connection_flush(conn); 243 244 dbus_message_unref(reply); 245 246 return DBUS_HANDLER_RESULT_HANDLED; 247} 248 249static DBusHandlerResult cancel_message(DBusConnection *conn, 250 DBusMessage *msg, void *data) 251{ 252 DBusMessage *reply; 253 254 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) { 255 fprintf(stderr, "Invalid arguments for passkey Confirm method"); 256 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 257 } 258 259 printf("Request canceled\n"); 260 261 reply = dbus_message_new_method_return(msg); 262 if (!reply) { 263 fprintf(stderr, "Can't create reply message\n"); 264 return DBUS_HANDLER_RESULT_NEED_MEMORY; 265 } 266 267 dbus_connection_send(conn, reply, NULL); 268 269 dbus_connection_flush(conn); 270 271 dbus_message_unref(reply); 272 273 return DBUS_HANDLER_RESULT_HANDLED; 274} 275 276static DBusHandlerResult release_message(DBusConnection *conn, 277 DBusMessage *msg, void *data) 278{ 279 DBusMessage *reply; 280 281 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) { 282 fprintf(stderr, "Invalid arguments for Release method"); 283 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 284 } 285 286 if (!__io_canceled) 287 fprintf(stderr, "Agent has been released\n"); 288 289 __io_terminated = 1; 290 291 reply = dbus_message_new_method_return(msg); 292 if (!reply) { 293 fprintf(stderr, "Can't create reply message\n"); 294 return DBUS_HANDLER_RESULT_NEED_MEMORY; 295 } 296 297 dbus_connection_send(conn, reply, NULL); 298 299 dbus_connection_flush(conn); 300 301 dbus_message_unref(reply); 302 303 return DBUS_HANDLER_RESULT_HANDLED; 304} 305 306static DBusHandlerResult agent_message(DBusConnection *conn, 307 DBusMessage *msg, void *data) 308{ 309 if (dbus_message_is_method_call(msg, "org.bluez.Agent", 310 "RequestPinCode")) 311 return request_pincode_message(conn, msg, data); 312 313 if (dbus_message_is_method_call(msg, "org.bluez.Agent", 314 "RequestPasskey")) 315 return request_passkey_message(conn, msg, data); 316 317 if (dbus_message_is_method_call(msg, "org.bluez.Agent", 318 "RequestConfirmation")) 319 return request_confirmation_message(conn, msg, data); 320 321 if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Authorize")) 322 return authorize_message(conn, msg, data); 323 324 if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Cancel")) 325 return cancel_message(conn, msg, data); 326 327 if (dbus_message_is_method_call(msg, "org.bluez.Agent", "Release")) 328 return release_message(conn, msg, data); 329 330 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 331} 332 333static const DBusObjectPathVTable agent_table = { 334 .message_function = agent_message, 335}; 336 337static int register_agent(DBusConnection *conn, const char *adapter_path, 338 const char *agent_path, 339 const char *capabilities) 340{ 341 DBusMessage *msg, *reply; 342 DBusError err; 343 344 msg = dbus_message_new_method_call("org.bluez", adapter_path, 345 "org.bluez.Adapter", "RegisterAgent"); 346 if (!msg) { 347 fprintf(stderr, "Can't allocate new method call\n"); 348 return -1; 349 } 350 351 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path, 352 DBUS_TYPE_STRING, &capabilities, 353 DBUS_TYPE_INVALID); 354 355 dbus_error_init(&err); 356 357 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); 358 359 dbus_message_unref(msg); 360 361 if (!reply) { 362 fprintf(stderr, "Can't register agent\n"); 363 if (dbus_error_is_set(&err)) { 364 fprintf(stderr, "%s\n", err.message); 365 dbus_error_free(&err); 366 } 367 return -1; 368 } 369 370 dbus_message_unref(reply); 371 372 dbus_connection_flush(conn); 373 374 return 0; 375} 376 377static int unregister_agent(DBusConnection *conn, const char *adapter_path, 378 const char *agent_path) 379{ 380 DBusMessage *msg, *reply; 381 DBusError err; 382 383 msg = dbus_message_new_method_call("org.bluez", adapter_path, 384 "org.bluez.Adapter", "UnregisterAgent"); 385 if (!msg) { 386 fprintf(stderr, "Can't allocate new method call\n"); 387 return -1; 388 } 389 390 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path, 391 DBUS_TYPE_INVALID); 392 393 dbus_error_init(&err); 394 395 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); 396 397 dbus_message_unref(msg); 398 399 if (!reply) { 400 fprintf(stderr, "Can't unregister agent\n"); 401 if (dbus_error_is_set(&err)) { 402 fprintf(stderr, "%s\n", err.message); 403 dbus_error_free(&err); 404 } 405 return -1; 406 } 407 408 dbus_message_unref(reply); 409 410 dbus_connection_flush(conn); 411 412 dbus_connection_unregister_object_path(conn, agent_path); 413 414 return 0; 415} 416 417static int create_paired_device(DBusConnection *conn, const char *adapter_path, 418 const char *agent_path, 419 const char *capabilities, 420 const char *device) 421{ 422 dbus_bool_t success; 423 DBusMessage *msg; 424 425 msg = dbus_message_new_method_call("org.bluez", adapter_path, 426 "org.bluez.Adapter", 427 "CreatePairedDevice"); 428 if (!msg) { 429 fprintf(stderr, "Can't allocate new method call\n"); 430 return -1; 431 } 432 433 dbus_message_append_args(msg, DBUS_TYPE_STRING, &device, 434 DBUS_TYPE_OBJECT_PATH, &agent_path, 435 DBUS_TYPE_STRING, &capabilities, 436 DBUS_TYPE_INVALID); 437 438 success = dbus_connection_send(conn, msg, NULL); 439 440 dbus_message_unref(msg); 441 442 if (!success) { 443 fprintf(stderr, "Not enough memory for message send\n"); 444 return -1; 445 } 446 447 dbus_connection_flush(conn); 448 449 return 0; 450} 451 452static char *get_default_adapter_path(DBusConnection *conn) 453{ 454 DBusMessage *msg, *reply; 455 DBusError err; 456 const char *reply_path; 457 char *path; 458 459 msg = dbus_message_new_method_call("org.bluez", "/", 460 "org.bluez.Manager", "DefaultAdapter"); 461 462 if (!msg) { 463 fprintf(stderr, "Can't allocate new method call\n"); 464 return NULL; 465 } 466 467 dbus_error_init(&err); 468 469 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); 470 471 dbus_message_unref(msg); 472 473 if (!reply) { 474 fprintf(stderr, 475 "Can't get default adapter\n"); 476 if (dbus_error_is_set(&err)) { 477 fprintf(stderr, "%s\n", err.message); 478 dbus_error_free(&err); 479 } 480 return NULL; 481 } 482 483 if (!dbus_message_get_args(reply, &err, 484 DBUS_TYPE_OBJECT_PATH, &reply_path, 485 DBUS_TYPE_INVALID)) { 486 fprintf(stderr, 487 "Can't get reply arguments\n"); 488 if (dbus_error_is_set(&err)) { 489 fprintf(stderr, "%s\n", err.message); 490 dbus_error_free(&err); 491 } 492 return NULL; 493 } 494 495 path = strdup(reply_path); 496 497 dbus_message_unref(reply); 498 499 dbus_connection_flush(conn); 500 501 return path; 502} 503 504static char *get_adapter_path(DBusConnection *conn, const char *adapter) 505{ 506 DBusMessage *msg, *reply; 507 DBusError err; 508 const char *reply_path; 509 char *path; 510 511 if (!adapter) 512 return get_default_adapter_path(conn); 513 514 msg = dbus_message_new_method_call("org.bluez", "/", 515 "org.bluez.Manager", "FindAdapter"); 516 517 if (!msg) { 518 fprintf(stderr, "Can't allocate new method call\n"); 519 return NULL; 520 } 521 522 dbus_message_append_args(msg, DBUS_TYPE_STRING, &adapter, 523 DBUS_TYPE_INVALID); 524 525 dbus_error_init(&err); 526 527 reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err); 528 529 dbus_message_unref(msg); 530 531 if (!reply) { 532 fprintf(stderr, 533 "Can't find adapter %s\n", adapter); 534 if (dbus_error_is_set(&err)) { 535 fprintf(stderr, "%s\n", err.message); 536 dbus_error_free(&err); 537 } 538 return NULL; 539 } 540 541 if (!dbus_message_get_args(reply, &err, 542 DBUS_TYPE_OBJECT_PATH, &reply_path, 543 DBUS_TYPE_INVALID)) { 544 fprintf(stderr, 545 "Can't get reply arguments\n"); 546 if (dbus_error_is_set(&err)) { 547 fprintf(stderr, "%s\n", err.message); 548 dbus_error_free(&err); 549 } 550 return NULL; 551 } 552 553 path = strdup(reply_path); 554 555 dbus_message_unref(reply); 556 557 dbus_connection_flush(conn); 558 559 return path; 560} 561 562static void usage(void) 563{ 564 printf("Bluetooth agent ver %s\n\n", VERSION); 565 566 printf("Usage:\n" 567 "\tagent [--adapter adapter-path] [--path agent-path] <passkey> [<device>]\n" 568 "\n"); 569} 570 571static struct option main_options[] = { 572 { "adapter", 1, 0, 'a' }, 573 { "path", 1, 0, 'p' }, 574 { "capabilites",1, 0, 'c' }, 575 { "delay", 1, 0, 'd' }, 576 { "reject", 0, 0, 'r' }, 577 { "help", 0, 0, 'h' }, 578 { 0, 0, 0, 0 } 579}; 580 581int main(int argc, char *argv[]) 582{ 583 const char *capabilities = "DisplayYesNo"; 584 struct sigaction sa; 585 DBusConnection *conn; 586 char match_string[128], default_path[128], *adapter_id = NULL; 587 char *adapter_path = NULL, *agent_path = NULL, *device = NULL; 588 int opt; 589 590 snprintf(default_path, sizeof(default_path), 591 "/org/bluez/agent_%d", getpid()); 592 593 while ((opt = getopt_long(argc, argv, "+a:p:c:d:rh", main_options, NULL)) != EOF) { 594 switch(opt) { 595 case 'a': 596 adapter_id = optarg; 597 break; 598 case 'p': 599 if (optarg[0] != '/') { 600 fprintf(stderr, "Invalid path\n"); 601 exit(1); 602 } 603 agent_path = strdup(optarg); 604 break; 605 case 'c': 606 capabilities = optarg; 607 break; 608 case 'd': 609 passkey_delay = atoi(optarg); 610 break; 611 case 'r': 612 do_reject = 1; 613 break; 614 case 'h': 615 usage(); 616 exit(0); 617 default: 618 exit(1); 619 } 620 } 621 622 argc -= optind; 623 argv += optind; 624 optind = 0; 625 626 if (argc < 1) { 627 usage(); 628 exit(1); 629 } 630 631 passkey_value = strdup(argv[0]); 632 633 if (argc > 1) 634 device = strdup(argv[1]); 635 636 if (!agent_path) 637 agent_path = strdup(default_path); 638 639 conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); 640 if (!conn) { 641 fprintf(stderr, "Can't get on system bus"); 642 exit(1); 643 } 644 645 adapter_path = get_adapter_path(conn, adapter_id); 646 if (!adapter_path) 647 exit(1); 648 649 if (!dbus_connection_register_object_path(conn, agent_path, 650 &agent_table, NULL)) { 651 fprintf(stderr, "Can't register object path for agent\n"); 652 exit(1); 653 } 654 655 if (device) { 656 if (create_paired_device(conn, adapter_path, agent_path, 657 capabilities, device) < 0) { 658 dbus_connection_unref(conn); 659 exit(1); 660 } 661 } else { 662 if (register_agent(conn, adapter_path, agent_path, 663 capabilities) < 0) { 664 dbus_connection_unref(conn); 665 exit(1); 666 } 667 } 668 669 if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL)) 670 fprintf(stderr, "Can't add signal filter"); 671 672 snprintf(match_string, sizeof(match_string), 673 "interface=%s,member=NameOwnerChanged,arg0=%s", 674 DBUS_INTERFACE_DBUS, "org.bluez"); 675 676 dbus_bus_add_match(conn, match_string, NULL); 677 678 memset(&sa, 0, sizeof(sa)); 679 sa.sa_flags = SA_NOCLDSTOP; 680 sa.sa_handler = sig_term; 681 sigaction(SIGTERM, &sa, NULL); 682 sigaction(SIGINT, &sa, NULL); 683 684 while (!__io_canceled && !__io_terminated) { 685 if (dbus_connection_read_write_dispatch(conn, 500) != TRUE) 686 break; 687 } 688 689 if (!__io_terminated && !device) 690 unregister_agent(conn, adapter_path, agent_path); 691 692 free(adapter_path); 693 free(agent_path); 694 695 free(passkey_value); 696 697 dbus_connection_unref(conn); 698 699 return 0; 700} 701