|
5 | 5 | #include <libgen.h>
|
6 | 6 | #include <pthread.h>
|
7 | 7 | #include <signal.h>
|
| 8 | +#include <stdbool.h> |
8 | 9 | #include <stdio.h>
|
9 | 10 | #include <stdlib.h>
|
10 | 11 | #include <string.h>
|
|
16 | 17 | #include <sys/wait.h>
|
17 | 18 | #include <systemd/sd-bus-protocol.h>
|
18 | 19 | #include <systemd/sd-bus.h>
|
| 20 | +#include <systemd/sd-event.h> |
19 | 21 | #include <unistd.h>
|
20 | 22 | #include <wpa_ctrl.h>
|
21 | 23 |
|
|
29 | 31 | #define DBUS_PROP_IFACE "org.freedesktop.DBus.Properties"
|
30 | 32 | #define NM_BUS_NAME "org.freedesktop.NetworkManager"
|
31 | 33 | #define NM_BUS_DEVICE_IFACE NM_BUS_NAME ".Device"
|
| 34 | +#define NM_BUS_PATH "/org/freedesktop/NetworkManager" |
32 | 35 |
|
33 | 36 | const char *wpa_ctrl_interface = "/var/run/wpa_supplicant_drc";
|
34 | 37 |
|
@@ -345,25 +348,63 @@ void *read_stdin(void *)
|
345 | 348 | return NULL;
|
346 | 349 | }
|
347 | 350 |
|
| 351 | +static int get_networkmanager_device_path(sd_bus *bus, const char *wireless_interface, char **ret_path) |
| 352 | +{ |
| 353 | + int r; |
| 354 | + |
| 355 | + CLEANUP(sd_bus_message_unrefp) sd_bus_message *response = NULL; |
| 356 | + if ((r = sd_bus_call_method(bus, NM_BUS_NAME, NM_BUS_PATH, NM_BUS_NAME, "GetDeviceByIpIface", NULL, &response, "s", wireless_interface)) < 0) |
| 357 | + return r; |
| 358 | + |
| 359 | + char *path; |
| 360 | + if ((r = sd_bus_message_read_basic(response, SD_BUS_TYPE_OBJECT_PATH, &path)) < 0) |
| 361 | + return r; |
| 362 | + |
| 363 | + if ((*ret_path = strdup(path)) == NULL) { |
| 364 | + return -ENOMEM; |
| 365 | + } |
| 366 | + |
| 367 | + return 0; |
| 368 | +} |
| 369 | + |
348 | 370 | void *wpa_setup_environment(void *data)
|
349 | 371 | {
|
350 | 372 | void *ret = THREADRESULT(VANILLA_ERROR);
|
351 | 373 |
|
352 | 374 | struct sync_args *args = (struct sync_args *) data;
|
353 | 375 |
|
354 |
| - // Check status of interface with NetworkManager |
355 |
| - int is_managed = 0; |
356 |
| - if (is_networkmanager_managing_device(args->wireless_interface, &is_managed) != VANILLA_SUCCESS) { |
357 |
| - print_info("FAILED TO DETERMINE MANAGED STATE OF WIRELESS INTERFACE"); |
358 |
| - //goto die; |
359 |
| - } |
| 376 | + CLEANUP(sd_event_unrefp) sd_event *bus_loop = NULL; |
| 377 | + if (sd_event_new(&bus_loop) < 0) |
| 378 | + print_info("FAILED TO CREATE EVENT LOOP"); |
360 | 379 |
|
361 |
| - // If NetworkManager is managing this device, temporarily stop it from doing so |
362 |
| - if (is_managed) { |
363 |
| - if (disable_networkmanager_on_device(args->wireless_interface) != VANILLA_SUCCESS) { |
364 |
| - print_info("FAILED TO SET %s TO UNMANAGED, RESULTS MAY BE UNPREDICTABLE"); |
| 380 | + CLEANUP(sd_bus_unrefp) sd_bus *bus = NULL; |
| 381 | + if (bus_loop && sd_bus_default_system(&bus) < 0) |
| 382 | + print_info("FAILED TO CONNECT TO SYSTEM BUS, EXPECT THINGS TO NOT WORK"); |
| 383 | + |
| 384 | + if (bus && sd_bus_attach_event(bus, bus_loop, SD_EVENT_PRIORITY_NORMAL) < 0) |
| 385 | + print_info("FAILED TO ATTACH BUS TO EVENT LOOP"); |
| 386 | + |
| 387 | + CLEANUP(freep) char *connection = NULL; |
| 388 | + CLEANUP(freep) char *nm_device = NULL; |
| 389 | + int is_managed = 0; |
| 390 | + |
| 391 | + if (bus) { |
| 392 | + if (get_networkmanager_device_path(bus, args->wireless_interface, &nm_device) < 0) { |
| 393 | + print_info("FAILED TO CONNECT TO NETWORKMANAGER, RESULTS MAY BE UNPREDICTABLE"); |
365 | 394 | } else {
|
366 |
| - print_info("TEMPORARILY SET %s TO UNMANAGED", args->wireless_interface); |
| 395 | + // Check status of interface with NetworkManager |
| 396 | + if (is_networkmanager_managing_device(bus, nm_device, &is_managed) != VANILLA_SUCCESS) { |
| 397 | + print_info("FAILED TO DETERMINE MANAGED STATE OF WIRELESS INTERFACE"); |
| 398 | + } |
| 399 | + |
| 400 | + // If NetworkManager is managing this device, temporarily stop it from doing so |
| 401 | + if (is_managed) { |
| 402 | + if (disable_networkmanager_on_device(bus, bus_loop, nm_device, &connection) != VANILLA_SUCCESS) { |
| 403 | + print_info("FAILED TO SET %s TO UNMANAGED, RESULTS MAY BE UNPREDICTABLE"); |
| 404 | + } else { |
| 405 | + print_info("TEMPORARILY SET %s TO UNMANAGED", args->wireless_interface); |
| 406 | + } |
| 407 | + } |
367 | 408 | }
|
368 | 409 | }
|
369 | 410 |
|
@@ -408,7 +449,7 @@ void *wpa_setup_environment(void *data)
|
408 | 449 | die_and_reenable_managed:
|
409 | 450 | if (is_managed) {
|
410 | 451 | print_info("SETTING %s BACK TO MANAGED", args->wireless_interface);
|
411 |
| - enable_networkmanager_on_device(args->wireless_interface); |
| 452 | + enable_networkmanager_on_device(bus, bus_loop, nm_device, connection); |
412 | 453 | }
|
413 | 454 |
|
414 | 455 | die:
|
@@ -456,87 +497,198 @@ int call_dhcp(const char *network_interface, pid_t *dhclient_pid)
|
456 | 497 | }
|
457 | 498 | }
|
458 | 499 |
|
459 |
| -static int get_networkmanager_device_path(sd_bus *bus, const char *wireless_interface, char **ret_path, sd_bus_error *ret_error) |
| 500 | +int is_networkmanager_managing_device(sd_bus *bus, const char *nm_device, int *is_managed) |
460 | 501 | {
|
461 |
| - int r; |
462 |
| - |
463 |
| - CLEANUP(sd_bus_message_unrefp) sd_bus_message *response = NULL; |
464 |
| - if ((r = sd_bus_call_method(bus, NM_BUS_NAME, "/org/freedesktop/NetworkManager", NM_BUS_NAME, "GetDeviceByIpIface", ret_error, &response, "s", wireless_interface)) < 0) |
465 |
| - return r; |
| 502 | + CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; |
466 | 503 |
|
467 |
| - char *path; |
468 |
| - r = sd_bus_message_read_basic(response, SD_BUS_TYPE_OBJECT_PATH, &path); |
469 |
| - if ((*ret_path = strdup(path)) == NULL) { |
470 |
| - print_info("FAILED TO ALLOCATE MEMORY"); |
471 |
| - return sd_bus_error_set_errno(ret_error, ENOMEM); |
| 504 | + if (sd_bus_get_property_trivial(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "Managed", &err, SD_BUS_TYPE_BOOLEAN, is_managed) < 0) { |
| 505 | + print_info("FAILED TO DETERMINE WHETHER NETWORKMANAGER IS MANAGING %s: %s", nm_device, err.message); |
| 506 | + return VANILLA_ERROR; |
472 | 507 | }
|
473 |
| - return r; |
| 508 | + |
| 509 | + return VANILLA_SUCCESS; |
474 | 510 | }
|
475 | 511 |
|
476 |
| -int is_networkmanager_managing_device(const char *wireless_interface, int *is_managed) |
| 512 | +static int bus_get_property_opath(sd_bus *bus, const char *destination, const char *path, const char *interface, const char *member, sd_bus_error *err, char **ret) |
477 | 513 | {
|
478 | 514 | int r;
|
479 |
| - CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; |
480 | 515 |
|
481 |
| - CLEANUP(sd_bus_unrefp) sd_bus *bus = NULL; |
482 |
| - if ((r = sd_bus_default_system(&bus)) < 0) { |
483 |
| - print_info("FAILED TO CONNECT TO SYSTEM BUS: %i", -r); |
484 |
| - return VANILLA_ERROR; |
485 |
| - } |
| 516 | + CLEANUP(sd_bus_message_unrefp) sd_bus_message *reply = NULL; |
| 517 | + if ((r = sd_bus_get_property(bus, destination, path, interface, member, err, &reply, "o")) < 0) |
| 518 | + return r; |
486 | 519 |
|
487 |
| - CLEANUP(freep) char *path = NULL; |
488 |
| - if ((r = get_networkmanager_device_path(bus, wireless_interface, &path, &err)) == -EHOSTUNREACH) { |
489 |
| - // NetworkManager doesn't seem to be running |
490 |
| - print_info("FAILED TO CONNECT TO NETWORKMANAGER, RESULTS MAY BE UNPREDICTABLE"); |
491 |
| - *is_managed = 0; |
492 |
| - return VANILLA_SUCCESS; |
493 |
| - } else if (r < 0) |
| 520 | + const char *s; |
| 521 | + if ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_OBJECT_PATH, &s)) < 0) |
494 | 522 | goto fail;
|
495 | 523 |
|
496 |
| - if (sd_bus_get_property_trivial(bus, NM_BUS_NAME, path, NM_BUS_DEVICE_IFACE, "Managed", &err, SD_BUS_TYPE_BOOLEAN, is_managed) < 0) |
| 524 | + if (!(*ret = strdup(s))) { |
| 525 | + r = errno; |
497 | 526 | goto fail;
|
| 527 | + } |
498 | 528 |
|
499 |
| - return VANILLA_SUCCESS; |
| 529 | + return 0; |
500 | 530 |
|
501 | 531 | fail:
|
502 |
| - print_info("FAILED TO DETERMINE WHETHER NETWORKMANAGER IS MANAGING %s: %s", wireless_interface, err.message); |
503 |
| - return VANILLA_ERROR; |
| 532 | + return sd_bus_error_set_errno(err, r); |
504 | 533 | }
|
505 | 534 |
|
506 |
| -static int set_networkmanager_on_device(const char *wireless_interface, int on) |
| 535 | +#define NM_DEVICE_STATE_DISCONNECTED 30 |
| 536 | +#define NM_DEVICE_STATE_DEACTIVATING 110 |
| 537 | + |
| 538 | +static int _nm_bus_state_signal_cb(sd_bus_message *msg, void *data, sd_bus_error *err, bool connecting) |
507 | 539 | {
|
| 540 | + bool *complete = data; |
| 541 | + uint32_t state; |
508 | 542 | int r;
|
509 |
| - CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; |
510 |
| - CLEANUP(sd_bus_message_unrefp) sd_bus_message *call = NULL; |
511 | 543 |
|
512 |
| - CLEANUP(sd_bus_unrefp) sd_bus *bus = NULL; |
513 |
| - if ((r = sd_bus_default_system(&bus)) < 0) { |
514 |
| - print_info("FAILED TO CONNECT TO SYSTEM BUS: %i", -r); |
515 |
| - return VANILLA_ERROR; |
| 544 | + r = sd_bus_message_read(msg, "u", &state); |
| 545 | + if (r < 0) |
| 546 | + return sd_bus_error_set_errno(err, r); |
| 547 | + |
| 548 | + if (connecting && state == NM_DEVICE_STATE_DISCONNECTED) { |
| 549 | + print_info("remanaged!"); |
| 550 | + *complete = true; |
| 551 | + return 0; |
| 552 | + } else if (connecting) |
| 553 | + goto fail; |
| 554 | + else if (state == NM_DEVICE_STATE_DISCONNECTED) { |
| 555 | + print_info("disconnected!"); |
| 556 | + *complete = true; |
| 557 | + return 0; |
| 558 | + } else if (state == NM_DEVICE_STATE_DEACTIVATING) |
| 559 | + /* It's still disconnecting, let's wait */ |
| 560 | + return 0; |
| 561 | + else |
| 562 | + goto fail; |
| 563 | + |
| 564 | +fail: |
| 565 | + print_info("UNEXPECTED NM STATUS: %u", state); |
| 566 | + return sd_bus_error_set_errno(err, EINVAL); |
| 567 | +} |
| 568 | + |
| 569 | +static int nm_bus_state_signal_disconn_cb(sd_bus_message *msg, void *data, sd_bus_error *err) |
| 570 | +{ |
| 571 | + return _nm_bus_state_signal_cb(msg, data, err, false); |
| 572 | +} |
| 573 | + |
| 574 | +static int nm_bus_state_signal_conn_cb(sd_bus_message *msg, void *data, sd_bus_error *err) |
| 575 | +{ |
| 576 | + return _nm_bus_state_signal_cb(msg, data, err, true); |
| 577 | +} |
| 578 | + |
| 579 | +static int nm_bus_register_state_signal(sd_bus *bus, const char *path, bool *complete, sd_bus_message_handler_t callback, sd_bus_error *err, sd_bus_slot **ret_slot) |
| 580 | +{ |
| 581 | + int r; |
| 582 | + |
| 583 | + r = sd_bus_match_signal(bus, ret_slot, NM_BUS_NAME, path, NM_BUS_DEVICE_IFACE, "StateChanged", callback, complete); |
| 584 | + if (r < 0) |
| 585 | + return sd_bus_error_set_errno(err, r); |
| 586 | + |
| 587 | + return 0; |
| 588 | +} |
| 589 | + |
| 590 | +static uint64_t get_time_µsecs() |
| 591 | +{ |
| 592 | + struct timespec cur_time; |
| 593 | + clock_gettime(CLOCK_MONOTONIC_COARSE, &cur_time); |
| 594 | + return cur_time.tv_sec * 1000 * 1000 + cur_time.tv_nsec / 1000; |
| 595 | +} |
| 596 | + |
| 597 | +static int nm_bus_await_signal(sd_bus *bus, sd_event *loop, bool *complete, sd_bus_slot *slot, sd_bus_error *err) |
| 598 | +{ |
| 599 | + /* Give it ten seconds, should it be longer? */ |
| 600 | + const uint64_t time_allowed = 10 * 1000 * 1000; |
| 601 | + |
| 602 | + const uint64_t deadline = get_time_µsecs() + time_allowed; |
| 603 | + int r = 0; |
| 604 | + |
| 605 | + while (!complete) { |
| 606 | + const uint64_t timeout = (deadline - get_time_µsecs()); |
| 607 | + if (timeout > time_allowed) |
| 608 | + /* underflow occured, i.e. we timed out */ |
| 609 | + return sd_bus_error_set_errno(err, ETIMEDOUT); |
| 610 | + |
| 611 | + r = sd_event_run(loop, timeout); |
| 612 | + if (r < 0) |
| 613 | + return sd_bus_error_set_errno(err, r); |
516 | 614 | }
|
517 | 615 |
|
518 |
| - CLEANUP(freep) char *path = NULL; |
519 |
| - if (get_networkmanager_device_path(bus, wireless_interface, &path, &err) < 0) |
| 616 | + return 0; |
| 617 | +} |
| 618 | + |
| 619 | +int disable_networkmanager_on_device(sd_bus *bus, sd_event* loop, const char *nm_device, char **ret_connection) |
| 620 | +{ |
| 621 | + CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; |
| 622 | + CLEANUP(sd_bus_slot_unrefp) sd_bus_slot *bus_slot = NULL; |
| 623 | + CLEANUP(freep) char *active_conn = NULL; |
| 624 | + |
| 625 | + /* Get the active connection object of the Wi-Fi adapter */ |
| 626 | + if (bus_get_property_opath(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "ActiveConnection", &err, &active_conn) < 0) |
| 627 | + goto fail; |
| 628 | + |
| 629 | + /* Get the *actual* connection object from the active connection object */ |
| 630 | + if (bus_get_property_opath(bus, NM_BUS_NAME, active_conn, NM_BUS_NAME ".Connection.Active", "Connection", &err, ret_connection) < 0) |
| 631 | + goto fail; |
| 632 | + |
| 633 | + /* We have to wait for NetworkManager to fully "unmanage" the device before |
| 634 | + * we tell it to disconnect the device, otherwise it will ignore the latter |
| 635 | + * request, so iwd won't disconnect on systems where it is used. */ |
| 636 | + |
| 637 | + bool disconnect_done = false; |
| 638 | + |
| 639 | + /* Set up signal handler so we can know exactly when NetworkManager has |
| 640 | + * finished disconnecting the device. */ |
| 641 | + if (nm_bus_register_state_signal(bus, nm_device, &disconnect_done, nm_bus_state_signal_disconn_cb, &err, &bus_slot) < 0) |
520 | 642 | goto fail;
|
521 | 643 |
|
522 |
| - if (sd_bus_set_property(bus, NM_BUS_NAME, path, NM_BUS_DEVICE_IFACE, "Managed", &err, "b", on) < 0) |
| 644 | + /* Tell NM to disconnect the device */ |
| 645 | + if (sd_bus_call_method(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "Disconnect", &err, NULL, "") < 0) |
| 646 | + goto fail; |
| 647 | + |
| 648 | + /* Wait for NM to have disconnected the device */ |
| 649 | + if (nm_bus_await_signal(bus, loop, &disconnect_done, bus_slot, &err) < 0) |
| 650 | + goto fail; |
| 651 | + |
| 652 | + /* Tell NM to unmanage the device */ |
| 653 | + if (sd_bus_set_property(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "Managed", &err, "b", 0) < 0) |
523 | 654 | goto fail;
|
524 | 655 |
|
525 | 656 | return VANILLA_SUCCESS;
|
526 | 657 |
|
527 | 658 | fail:
|
528 |
| - print_info("FAILED TO SET MANAGEMENT OVER NETWORKMANAGER INTERFACE %s: %s", wireless_interface, err.message); |
| 659 | + print_info("FAILED TO DISABLE NETWORKMANAGER ON INTERFACE %s: %s", nm_device, err.message); |
529 | 660 | return VANILLA_ERROR;
|
530 | 661 | }
|
531 | 662 |
|
532 |
| -int disable_networkmanager_on_device(const char *wireless_interface) |
| 663 | +int enable_networkmanager_on_device(sd_bus *bus, sd_event* loop, const char *nm_device, const char *resume_connection) |
533 | 664 | {
|
534 |
| - return set_networkmanager_on_device(wireless_interface, 0); |
535 |
| -} |
| 665 | + CLEANUP(sd_bus_error_free) sd_bus_error err = SD_BUS_ERROR_NULL; |
| 666 | + CLEANUP(sd_bus_slot_unrefp) sd_bus_slot *bus_slot = NULL; |
| 667 | + CLEANUP(freep) char *active_conn = NULL; |
| 668 | + bool manage_done = false; |
536 | 669 |
|
537 |
| -int enable_networkmanager_on_device(const char *wireless_interface) |
538 |
| -{ |
539 |
| - return set_networkmanager_on_device(wireless_interface, 1); |
| 670 | + /* Set up signal handler so we can know exactly when NetworkManager has |
| 671 | + * finished managing the device. */ |
| 672 | + if (nm_bus_register_state_signal(bus, nm_device, &manage_done, nm_bus_state_signal_conn_cb, &err, &bus_slot) < 0) |
| 673 | + goto fail; |
| 674 | + |
| 675 | + /* Tell NM to manage the device */ |
| 676 | + if (sd_bus_set_property(bus, NM_BUS_NAME, nm_device, NM_BUS_DEVICE_IFACE, "Managed", &err, "b", 1) < 0) |
| 677 | + goto fail; |
| 678 | + |
| 679 | + /* Wait for NM to have started managing the device */ |
| 680 | + if (nm_bus_await_signal(bus, loop, &manage_done, bus_slot, &err) < 0) |
| 681 | + goto fail; |
| 682 | + |
| 683 | + /* Finally, reconnect the device if it was originally connected */ |
| 684 | + if (strcmp(resume_connection, "/") && sd_bus_call_method(bus, NM_BUS_NAME, NM_BUS_PATH, NM_BUS_NAME, "ActivateConnection", &err, NULL, "ooo", resume_connection, nm_device, "/") < 0) |
| 685 | + goto fail; |
| 686 | + |
| 687 | + return VANILLA_SUCCESS; |
| 688 | + |
| 689 | +fail: |
| 690 | + print_info("FAILED TO ENABLE NETWORKMANAGER ON INTERFACE %s: %s", nm_device, err.message); |
| 691 | + return VANILLA_ERROR; |
540 | 692 | }
|
541 | 693 |
|
542 | 694 | void *do_relay(void *data)
|
|
0 commit comments