diff --git a/src/miracled-dbus.c b/src/miracled-dbus.c index ee4d5f6..df8b4ba 100644 --- a/src/miracled-dbus.c +++ b/src/miracled-dbus.c @@ -444,6 +444,25 @@ static int link_dbus_get_interface(sd_bus *bus, return 1; } +static int link_dbus_get_running(sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *data, + sd_bus_error *err) +{ + struct link *l = data; + int r, val; + + val = l->running; + r = sd_bus_message_append_basic(reply, 'b', &val); + if (r < 0) + return r; + + return 1; +} + static int link_dbus_get_name(sd_bus *bus, const char *path, const char *interface, @@ -503,6 +522,11 @@ static const sd_bus_vtable link_dbus_vtable[] = { link_dbus_get_interface, 0, SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("Running", + "b", + link_dbus_get_running, + 0, + SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_WRITABLE_PROPERTY("Name", "s", link_dbus_get_name, diff --git a/src/miracled-link.c b/src/miracled-link.c index 4ab5805..8ad6712 100644 --- a/src/miracled-link.c +++ b/src/miracled-link.c @@ -138,23 +138,17 @@ static void link_wifi_event_fn(struct wifi *w, void *data, } } -static int link_wifi_init(struct link *l) +static int link_wifi_start(struct link *l) { + _shl_cleanup_free_ char *path = NULL; struct wifi_dev *d; int r; - char *path; - r = wifi_new(l->m->event, link_wifi_event_fn, l, &l->w); - if (r < 0) - return r; - - path = shl_strcat("/run/wpa_supplicant/", l->interface); + path = shl_strjoin(arg_wpa_rundir, "/", l->interface, NULL); if (!path) return log_ENOMEM(); r = wifi_open(l->w, path); - free(path); - if (r < 0) return r; @@ -168,8 +162,104 @@ static int link_wifi_init(struct link *l) return 0; } +static int link_wifi_child_fn(sd_event_source *source, + const siginfo_t *si, + void *data) +{ + struct link *l = data; + + log_error("wpa_supplicant died unexpectedly on link %s", l->name); + + link_free(l); + + return 0; +} + +static int link_wifi_startup_fn(sd_event_source *source, + uint64_t usec, + void *data) +{ + struct link *l = data; + int r; + + r = link_wifi_start(l); + if (r < 0) { + if (wifi_is_open(l->w)) { + log_error("cannot start wifi on link %s", l->name); + link_free(l); + return 0; + } + + /* reschedule timer */ + sd_event_source_set_time(source, + now(CLOCK_MONOTONIC) + 200 * 1000); + sd_event_source_set_enabled(source, SD_EVENT_ON); + log_debug("wpa_supplicant startup still ongoing, reschedule.."); + return 0; + } + + log_debug("wpa_supplicant startup finished on link %s", l->name); + + sd_event_source_set_enabled(source, SD_EVENT_OFF); + l->running = true; + link_dbus_properties_changed(l, "Running", NULL); + + return 0; +} + +static int link_wifi_init(struct link *l) +{ + _shl_cleanup_free_ char *path = NULL; + int r; + + r = wifi_new(l->m->event, link_wifi_event_fn, l, &l->w); + if (r < 0) + return r; + + if (!arg_manage_wifi) { + r = link_wifi_start(l); + if (r < 0) + log_error("cannot open wpa_supplicant socket for link %s", + l->name); + else + l->running = true; + + return r; + } + + path = shl_strcat(arg_wpa_bindir, "/wpa_supplicant"); + if (!path) + return log_ENOMEM(); + + r = wifi_spawn_supplicant(l->w, arg_wpa_rundir, path, l->interface); + if (r < 0) + return r; + + r = sd_event_add_child(l->m->event, + wifi_get_supplicant_pid(l->w), + WEXITED, + link_wifi_child_fn, + l, + &l->wpa_child_source); + if (r < 0) + return r; + + r = sd_event_add_monotonic(l->m->event, + now(CLOCK_MONOTONIC) + 200 * 1000, + 0, + link_wifi_startup_fn, + l, + &l->wpa_startup_source); + if (r < 0) + return r; + + return 0; +} + static void link_wifi_destroy(struct link *l) { + sd_event_source_unref(l->wpa_startup_source); + sd_event_source_unref(l->wpa_child_source); wifi_close(l->w); wifi_free(l->w); } @@ -227,6 +317,7 @@ int link_new(struct manager *m, switch (l->type) { case LINK_VIRTUAL: + l->running = true; break; case LINK_WIFI: r = link_wifi_init(l); diff --git a/src/miracled-wifi.c b/src/miracled-wifi.c index 0e155a5..aa179b9 100644 --- a/src/miracled-wifi.c +++ b/src/miracled-wifi.c @@ -76,6 +76,8 @@ struct wifi { char *reply_buf; size_t reply_buf_size; + pid_t wpa_pid; + struct wfd_wpa_ctrl *wpa; sd_event_source *wpa_source; struct shl_dlist devs; @@ -784,10 +786,27 @@ error: void wifi_free(struct wifi *w) { + int r; + pid_t rp; + if (!w) return; wifi_close(w); + + if (w->wpa_pid > 0) { + log_debug("killing wpa_supplicant pid:%d and waiting for exit..", + w->wpa_pid); + r = kill(w->wpa_pid, SIGTERM); + if (r >= 0) + rp = waitpid(w->wpa_pid, NULL, 0); + if (r < 0 || rp != w->wpa_pid) { + r = kill(w->wpa_pid, SIGKILL); + if (r >= 0) + waitpid(w->wpa_pid, &r, 0); + } + } + sd_event_source_unref(w->wpa_source); wfd_wpa_ctrl_unref(w->wpa); free(w->reply_buf); @@ -832,6 +851,88 @@ static int wifi_read_all_peers(struct wifi *w) return (r == -EAGAIN) ? 0 : r; } +pid_t wifi_get_supplicant_pid(struct wifi *w) +{ + if (!w) + return 0; + + return w->wpa_pid; +} + +static void wifi_run_supplicant(struct wifi *w, + const char *rundir, + const char *binary, + const char *ifname) +{ + char *argv[64]; + int i; + sigset_t mask; + + sigemptyset(&mask); + sigprocmask(SIG_SETMASK, &mask, NULL); + + /* redirect stdout to stderr for wpa_supplicant */ + dup2(2, 1); + + /* initialize wpa_supplicant args */ + i = 0; + argv[i++] = (char*)binary; + argv[i++] = "-Dnl80211"; + argv[i++] = "-qq"; + argv[i++] = "-C"; + argv[i++] = (char*)rundir; + argv[i++] = "-i"; + argv[i++] = (char*)ifname; + argv[i++] = "-p p2p_device=1"; + argv[i] = NULL; + + /* execute wpa_supplicant; if it fails, the caller issues exit(1) */ + execve(argv[0], argv, environ); +} + +int wifi_spawn_supplicant(struct wifi *w, + const char *rundir, + const char *binary, + const char *ifname) +{ + pid_t pid; + + if (!w) + return log_EINVAL(); + if (w->wpa_pid > 0) + return 0; + + if (access(binary, X_OK) < 0) { + log_error("execution of wpa_supplicant-binary (%s) not allowed: %m", + binary); + return -EINVAL; + } + + pid = fork(); + if (pid < 0) { + return log_ERRNO(); + } else if (!pid) { + wifi_run_supplicant(w, rundir, binary, ifname); + exit(1); + } + + w->wpa_pid = pid; + log_info("waiting for wpa_supplicant (pid: %d) startup on %s..", + (int)pid, ifname); + + /* Ouh, yeah.. waiting for wpa_supplicant to start up is.. awful.. + * We could use inotify on the ctrl-socket/dir, but these might be + * left-overs from previous runs and thus might get destroyed first. + * And there're some more races.. so lets just do nothing and fire + * a 100ms timer repeatedly in the parent. This gives wpa_supplicant + * enough time to start up and we can test the connection from time to + * time. + * We just need to make sure to catch SIGCHLD so we stop trying if + * wpa_supplicant dies.. All done in the parent. */ + + return 0; +} + int wifi_open(struct wifi *w, const char *wpa_path) { int r; @@ -845,7 +946,7 @@ int wifi_open(struct wifi *w, const char *wpa_path) r = wfd_wpa_ctrl_open(w->wpa, wpa_path); if (r < 0) { - log_error("cannot open wpa_supplicant socket %s: %d", + log_debug("cannot open wpa_supplicant socket %s: %d", wpa_path, r); goto error; } @@ -880,7 +981,6 @@ void wifi_close(struct wifi *w) } wfd_wpa_ctrl_close(w->wpa); - w->wpa = NULL; } int wifi_set_discoverable(struct wifi *w, bool on) diff --git a/src/miracled-wifi.h b/src/miracled-wifi.h index 7df174f..f9322ca 100644 --- a/src/miracled-wifi.h +++ b/src/miracled-wifi.h @@ -97,6 +97,12 @@ void wifi_free(struct wifi *w); void wifi_set_data(struct wifi *w, void *data); void *wifi_get_data(struct wifi *w); +pid_t wifi_get_supplicant_pid(struct wifi *w); +int wifi_spawn_supplicant(struct wifi *w, + const char *rundir, + const char *binary, + const char *ifname); + bool wifi_is_open(struct wifi *w); int wifi_open(struct wifi *w, const char *wpa_path); void wifi_close(struct wifi *w); diff --git a/src/miracled.c b/src/miracled.c index 9d11d7d..0575f5c 100644 --- a/src/miracled.c +++ b/src/miracled.c @@ -44,6 +44,10 @@ #include "shl_log.h" #include "shl_util.h" +bool arg_manage_wifi = false; +const char *arg_wpa_rundir = "/run/wpa_supplicant"; +const char *arg_wpa_bindir = "/usr/bin"; + /* * Peer Handling */ @@ -406,13 +410,26 @@ static int manager_run(struct manager *m) static int help(void) { + /* + * 80-char barrier: + * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + */ printf("%s [OPTIONS...] ...\n\n" "Wifi-Display Daemon.\n\n" " -h --help Show this help\n" " --version Show package version\n" " --log-level Maximum level for log messages\n" " --log-time Prefix log-messages with timestamp\n" + "\n" + " --manage-wifi Enable internal wifi-management via wpa_supplicant if\n" + " they're not managed externally (like NetworkManager)\n" + " --wpa-rundir wpa_supplicant runtime dir [/run/wpa_supplicant]\n" + " --wpa-bindir wpa_supplicant binary dir [/usr/bin]\n" , program_invocation_short_name); + /* + * 80-char barrier: + * 01234567890123456789012345678901234567890123456789012345678901234567890123456789 + */ return 0; } @@ -423,12 +440,20 @@ static int parse_argv(int argc, char *argv[]) ARG_VERSION = 0x100, ARG_LOG_LEVEL, ARG_LOG_TIME, + + ARG_MANAGE_WIFI, + ARG_WPA_RUNDIR, + ARG_WPA_BINDIR, }; static const struct option options[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, ARG_VERSION }, { "log-level", required_argument, NULL, ARG_LOG_LEVEL }, { "log-time", no_argument, NULL, ARG_LOG_TIME }, + + { "manage-wifi", no_argument, NULL, ARG_MANAGE_WIFI }, + { "wpa-rundir", required_argument, NULL, ARG_WPA_RUNDIR }, + { "wpa-bindir", required_argument, NULL, ARG_WPA_BINDIR }, {} }; int c; @@ -446,6 +471,16 @@ static int parse_argv(int argc, char *argv[]) case ARG_LOG_TIME: log_init_time(); break; + + case ARG_MANAGE_WIFI: + arg_manage_wifi = true; + break; + case ARG_WPA_RUNDIR: + arg_wpa_rundir = optarg; + break; + case ARG_WPA_BINDIR: + arg_wpa_bindir = optarg; + break; case '?': return -EINVAL; } diff --git a/src/miracled.h b/src/miracled.h index 366dd8e..6cf2bb6 100644 --- a/src/miracled.h +++ b/src/miracled.h @@ -95,6 +95,10 @@ struct link { struct shl_dlist peers; struct wifi *w; + sd_event_source *wpa_child_source; + sd_event_source *wpa_startup_source; + + bool running : 1; }; #define link_from_htable(_l) \ @@ -166,4 +170,10 @@ void link_dbus_scan_stopped(struct link *l); void link_dbus_added(struct link *l); void link_dbus_removed(struct link *l); +/* cli arguments */ + +extern bool arg_manage_wifi; +extern const char *arg_wpa_rundir; +extern const char *arg_wpa_bindir; + #endif /* MIRACLED_H */