1 /*  RetroArch - A frontend for libretro.
2  *  Copyright (C) 2014-2017 - Jean-André Santoni
3  *
4  *  RetroArch is free software: you can redistribute it and/or modify it under the terms
5  *  of the GNU General Public License as published by the Free Software Found-
6  *  ation, either version 3 of the License, or (at your option) any later version.
7  *
8  *  RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9  *  without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
10  *  PURPOSE.  See the GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License along with RetroArch.
13  *  If not, see <http://www.gnu.org/licenses/>.
14  */
15 
16 #include <compat/strl.h>
17 #include <file/file_path.h>
18 #include <array/rbuf.h>
19 #include <lists/string_list.h>
20 #include <string/stdstring.h>
21 #include <retro_miscellaneous.h>
22 #include <configuration.h>
23 #include <verbosity.h>
24 #include <time.h>
25 #include "../wifi_driver.h"
26 #include "../../retroarch.h"
27 #include "../../lakka.h"
28 #ifdef HAVE_GFX_WIDGETS
29 #include "../../gfx/gfx_widgets.h"
30 #endif
31 
32 typedef struct
33 {
34    wifi_network_scan_t scan;
35    char command[300];
36    bool connmanctl_widgets_supported;
37 } connman_t;
38 
connmanctl_init(void)39 static void *connmanctl_init(void)
40 {
41    connman_t *connman = (connman_t*)calloc(1, sizeof(connman_t));
42 #ifdef HAVE_GFX_WIDGETS
43    connman->connmanctl_widgets_supported = gfx_widgets_ready();
44 #endif
45    return connman;
46 }
47 
connmanctl_free(void * data)48 static void connmanctl_free(void *data)
49 {
50    connman_t *connman = (connman_t*)data;
51    if (connman->scan.net_list)
52       RBUF_FREE(connman->scan.net_list);
53    if (data)
54       free(data);
55 }
56 
connmanctl_start(void * data)57 static bool connmanctl_start(void *data)
58 {
59    (void)data;
60    return true;
61 }
62 
connmanctl_stop(void * data)63 static void connmanctl_stop(void *data)
64 {
65    (void)data;
66 }
67 
connmanctl_refresh_services(connman_t * connman)68 static void connmanctl_refresh_services(connman_t *connman) {
69    char line[512];
70    FILE *serv_file = popen("connmanctl services", "r");
71 
72    if (connman->scan.net_list)
73       RBUF_FREE(connman->scan.net_list);
74 
75    while (fgets(line, 512, serv_file))
76    {
77       int i;
78       struct string_list* list = NULL;
79       wifi_network_info_t entry;
80       size_t len = strlen(line);
81       if (len > 0 && line[len-1] == '\n')
82          line[--len] = '\0';
83 
84       /* Parse lines directly and store net info directly */
85       memset(&entry, 0, sizeof(entry));
86       entry.connected = (line[2] == 'R' || line[2] == 'O');
87       entry.saved_password = (line[0] == '*');
88 
89       /* connmanctl services outputs a 4 character prefixed lines,
90        * either whitespace or an identifier. i.e.:
91        * $ connmanctl services
92        *     '*A0 SSID some_unique_id'
93        *     '    SSID some_another_unique_id'
94        */
95       list = string_split(&line[4], " ");
96       if (!list)
97          break;
98 
99       if (list->size == 0)
100          continue;
101 
102       for (i = 0; i < list->size-1; i++)
103       {
104          strlcat(entry.ssid, list->elems[i].data, sizeof(entry.ssid));
105          strlcat(entry.ssid, " ", sizeof(entry.ssid)-1);
106       }
107       if (strlen(entry.ssid))
108          entry.ssid[strlen(entry.ssid)-1] = 0;
109 
110       /* Store the connman network id here, for later */
111       strlcpy(entry.netid, list->elems[list->size-1].data, sizeof(entry.netid));
112       string_list_free(list);
113 
114       /* Filter only wifi nets */
115       if (!strncmp(entry.netid, "wifi_", 5))
116          RBUF_PUSH(connman->scan.net_list, entry);
117    }
118 
119    pclose(serv_file);
120 }
121 
connmanctl_enable(void * data,bool enabled)122 static bool connmanctl_enable(void *data, bool enabled)
123 {
124    connman_t *connman = (connman_t*)data;
125    if (enabled)
126       pclose(popen("connmanctl enable wifi", "r"));
127    else
128       pclose(popen("connmanctl disable wifi", "r"));
129 
130    /* Update the services, to ensure we properly show connection status */
131    connmanctl_refresh_services(connman);
132 
133    return true;
134 }
135 
connmanctl_tether_status(connman_t * connman)136 static bool connmanctl_tether_status(connman_t *connman)
137 {
138    /* Returns true if the tethering is active
139     * false when tethering is not active
140     */
141    size_t ln_size;
142    FILE *command_file = NULL;
143    char ln[3]         = {0};
144 
145    /* Following command lists 'technologies' of connman,
146     * greps the wifi + 10 following lines, then first
147     * occurance of 'Tethering', then 'True' and counts
148     * the matching lines.
149     * Expected result is either 1 (active) or 0 (not active)
150     */
151    snprintf(connman->command, sizeof(connman->command), "\
152          connmanctl technologies | \
153          grep \"/net/connman/technology/wifi\" -A 10 | \
154          grep \"^  Tethering =\" -m 1 | \
155          grep \"True\" | \
156          wc -l");
157 
158    command_file = popen(connman->command, "r");
159 
160    fgets(ln, sizeof(ln), command_file);
161 
162    ln_size = strlen(ln)-1;
163    if (ln[ln_size] == '\n')
164       ln[ln_size] = '\0';
165 
166    RARCH_LOG("[CONNMANCTL] Tether Status: command: \"%s\", output: \"%s\"\n",
167          connman->command, ln);
168 
169    pclose(command_file);
170 
171    if (!ln)
172       return false;
173    if (ln[0] == '0')
174       return false;
175    if (ln[0] == '1')
176       return true;
177    return false;
178 }
179 
connmanctl_tether_toggle(connman_t * connman,bool switch_on,char * apname,char * passkey)180 static void connmanctl_tether_toggle(
181       connman_t *connman, bool switch_on, char* apname, char* passkey)
182 {
183    /* Starts / stops the tethering service on wi-fi device */
184    char output[256]     = {0};
185    FILE *command_file   = NULL;
186    settings_t *settings = config_get_ptr();
187 #ifdef HAVE_GFX_WIDGETS
188    bool widgets_active  = connman->connmanctl_widgets_supported;
189 #endif
190 
191    snprintf(connman->command, sizeof(connman->command), "\
192          connmanctl tether wifi %s %s %s",
193          switch_on ? "on" : "off", apname, passkey);
194 
195    command_file = popen(connman->command, "r");
196 
197    RARCH_LOG("[CONNMANCTL] Tether toggle: command: \"%s\"\n",
198          connman->command);
199 
200    while (fgets(output, sizeof(output), command_file))
201    {
202       size_t output_size = strlen(output) - 1;
203       if (output[output_size] == '\n')
204          output[output_size] = '\0';
205 
206       RARCH_LOG("[CONNMANCTL] Tether toggle: output: \"%s\"\n",
207             output);
208 
209 #ifdef HAVE_GFX_WIDGETS
210       if (!widgets_active)
211 #endif
212          runloop_msg_queue_push(output, 1, 180, true,
213                NULL, MESSAGE_QUEUE_ICON_DEFAULT,
214                MESSAGE_QUEUE_CATEGORY_INFO);
215    }
216 
217    pclose(command_file);
218 
219    RARCH_LOG("[CONNMANCTL] Tether toggle: command finished\n");
220 
221    if (switch_on)
222    {
223       if (!connmanctl_tether_status(connman))
224          configuration_set_bool(settings,
225                settings->bools.localap_enable, false);
226    }
227    else
228    {
229       if (connmanctl_tether_status(connman))
230          configuration_set_bool(settings,
231                settings->bools.localap_enable, true);
232    }
233 }
234 
connmanctl_scan(void * data)235 static void connmanctl_scan(void *data)
236 {
237    settings_t *settings             = config_get_ptr();
238    connman_t *connman               = (connman_t*)data;
239 
240    if (connmanctl_tether_status(connman))
241    {
242       runloop_msg_queue_push(msg_hash_to_str(MSG_LOCALAP_SWITCHING_OFF),
243             1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
244             MESSAGE_QUEUE_CATEGORY_INFO);
245       configuration_set_bool(settings,
246             settings->bools.localap_enable, false);
247       connmanctl_tether_toggle(connman, false, "", "");
248    }
249 
250    pclose(popen("connmanctl scan wifi", "r"));
251 
252    runloop_msg_queue_push(msg_hash_to_str(MSG_WIFI_SCAN_COMPLETE),
253          1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
254          MESSAGE_QUEUE_CATEGORY_INFO);
255 
256    /* Refresh now the services, to read the discovered networks */
257    connman->scan.scan_time = time(NULL);
258    connmanctl_refresh_services(connman);
259 }
260 
connmanctl_get_ssids(void * data)261 static wifi_network_scan_t* connmanctl_get_ssids(void *data)
262 {
263    unsigned i;
264    connman_t *connman = (connman_t*)data;
265 
266    return &connman->scan;
267 }
268 
connmanctl_ssid_is_online(void * data,unsigned i)269 static bool connmanctl_ssid_is_online(void *data, unsigned i)
270 {
271    connman_t *connman = (connman_t*)data;
272    if (!connman->scan.net_list || i >= RBUF_LEN(connman->scan.net_list))
273       return false;
274    return connman->scan.net_list[i].connected;
275 }
276 
connmanctl_connection_info(void * data,wifi_network_info_t * netinfo)277 static bool connmanctl_connection_info(void *data, wifi_network_info_t *netinfo)
278 {
279    connman_t *connman = (connman_t*)data;
280    unsigned i;
281 
282    if (!connman->scan.net_list)
283       return false;
284 
285    for (i = 0; i < RBUF_LEN(connman->scan.net_list); i++)
286    {
287       if (connman->scan.net_list[i].connected)
288       {
289          if (netinfo)
290             memcpy(netinfo, &connman->scan.net_list[i], sizeof(*netinfo));
291          return true;
292       }
293    }
294 
295    return false;
296 }
297 
connmanctl_disconnect_ssid(void * data,const wifi_network_info_t * netinfo)298 static bool connmanctl_disconnect_ssid(void *data, const wifi_network_info_t* netinfo)
299 {
300    connman_t *connman = (connman_t*)data;
301 
302    /* TODO:Check whether this network is actually connected */
303 
304    snprintf(connman->command, sizeof(connman->command),
305          "connmanctl disconnect %s 2>&1",
306          netinfo->netid);
307 
308    pclose(popen(connman->command, "r"));
309 
310    /* Refresh the state since it has definitely changed */
311    connmanctl_refresh_services(connman);
312 
313    return true;
314 }
315 
connmanctl_connect_ssid(void * data,const wifi_network_info_t * netinfo)316 static bool connmanctl_connect_ssid(
317       void *data, const wifi_network_info_t *netinfo)
318 {
319    unsigned i;
320    bool success = false;
321    char settings_dir[PATH_MAX_LENGTH]  = {0};
322    char settings_path[PATH_MAX_LENGTH] = {0};
323    char netid[160]                     = {0};
324    FILE *settings_file                 = NULL;
325    connman_t *connman                  = (connman_t*)data;
326    settings_t *settings                = config_get_ptr();
327    static struct string_list* list     = NULL;
328 #ifdef HAVE_GFX_WIDGETS
329    bool widgets_active                 = connman->connmanctl_widgets_supported;
330 #endif
331    strlcat(netid, netinfo->netid, sizeof(netid));
332    strlcat(settings_dir, LAKKA_CONNMAN_DIR, sizeof(settings_dir));
333    strlcat(settings_dir, netid, sizeof(settings_dir));
334 
335    path_mkdir(settings_dir);
336 
337    strlcat(settings_path, settings_dir, sizeof(settings_path));
338    strlcat(settings_path, "/settings", sizeof(settings_path));
339 
340    if (!netinfo->saved_password)
341    {
342       settings_file = fopen(settings_path, "w");
343       if (!settings_file)
344          return false;
345       fprintf(settings_file, "[%s]\n", netid);
346       fprintf(settings_file, "Name=%s\n", netinfo->ssid);
347       fprintf(settings_file, "SSID=");
348 
349       for (i = 0; i < strlen(netinfo->ssid); i++)
350          fprintf(settings_file, "%02x", (unsigned int) netinfo->ssid[i]);
351       fprintf(settings_file, "\n");
352 
353       fprintf(settings_file, "Favorite=%s\n", "true");
354       fprintf(settings_file, "AutoConnect=%s\n", "true");
355       fprintf(settings_file, "Passphrase=%s\n", netinfo->passphrase);
356       fprintf(settings_file, "IPv4.method=%s\n", "dhcp");
357       fclose(settings_file);
358 
359       /* connman does not pick this up automatically, so hack: */
360       system("systemctl restart connman.service");
361    }
362    else
363    {
364       /* No need for pass, config should be there already, verify it */
365       settings_file = fopen(settings_path, "r");
366       if (!settings_file)
367       {
368          /* Usually a mismatch between connman state and config, reload */
369          system("systemctl restart connman.service");
370          return false;
371       }
372       fclose(settings_file);
373    }
374 
375    if (connmanctl_tether_status(connman))
376    {
377       runloop_msg_queue_push(msg_hash_to_str(MSG_LOCALAP_SWITCHING_OFF),
378             1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
379             MESSAGE_QUEUE_CATEGORY_INFO);
380       configuration_set_bool(settings,
381             settings->bools.localap_enable, false);
382       connmanctl_tether_toggle(connman, false, "", "");
383    }
384 
385    snprintf(connman->command, sizeof(connman->command),
386          "connmanctl connect %s",
387          netinfo->netid);
388 
389    pclose(popen(connman->command, "r"));
390 
391    /* Refresh status to reflect the updated state */
392    connmanctl_refresh_services(connman);
393 
394    /* connman is a PITA, return code is not meaningful at all :( */
395    for (i = 0; i < RBUF_LEN(connman->scan.net_list); i++)
396    {
397       if (!strcmp(netid, connman->scan.net_list[i].netid))
398       {
399          /* Found it! Check if we are connected now */
400          success = connman->scan.net_list[i].connected;
401          if (!success)
402          {
403             /* TODO: Add forget password option, which gets rid of this hack */
404             connman->scan.net_list[i].saved_password = false;
405             unlink(settings_path);
406          }
407       }
408    }
409 
410 #ifdef HAVE_GFX_WIDGETS
411    if (!widgets_active)
412 #endif
413    {
414       if (success)
415          runloop_msg_queue_push("Connected", 1, 180, true,
416                NULL, MESSAGE_QUEUE_ICON_DEFAULT,
417                MESSAGE_QUEUE_CATEGORY_INFO);
418       else
419          runloop_msg_queue_push("Connection failed!", 1, 180, true,
420                NULL, MESSAGE_QUEUE_ICON_DEFAULT,
421                MESSAGE_QUEUE_CATEGORY_INFO);
422    }
423 
424    return success;
425 }
426 
connmanctl_get_connected_ssid(connman_t * connman,char * ssid,size_t buffersize)427 static void connmanctl_get_connected_ssid(
428       connman_t *connman, char* ssid, size_t buffersize)
429 {
430    size_t ssid_size;
431    /* Stores the SSID of the currently connected Wi-Fi
432     * network in ssid
433     */
434    FILE *command_file = NULL;
435 
436    if (buffersize < 1)
437       return;
438 
439    /* Following command lists all connman services, greps
440     * only 'wifi_' services, then greps the one with
441     * 'R' (Ready) or 'O' (Online) flag and cuts out the ssid
442     */
443    snprintf(connman->command, sizeof(connman->command),
444          "connmanctl services | "
445          "grep wifi_ | "
446          "grep \"^..\\(R\\|O\\)\" | "
447          "cut -d' ' -f 2");
448 
449    command_file = popen(connman->command, "r");
450 
451    fgets(ssid, buffersize, command_file);
452 
453    pclose(command_file);
454 
455    ssid_size = strlen(ssid) - 1;
456 
457    if ((ssid_size + 1) > 0)
458       if (ssid[ssid_size] == '\n')
459          ssid[ssid_size] = '\0';
460 
461    RARCH_LOG("[CONNMANCTL] Get Connected SSID: command: \"%s\", output: \"%s\"\n",
462          connman->command, (ssid_size + 1) ? ssid : "<nothing_found>");
463 }
464 
connmanctl_get_connected_servicename(connman_t * connman,char * servicename,size_t buffersize)465 static void connmanctl_get_connected_servicename(
466       connman_t *connman, char* servicename, size_t buffersize)
467 {
468    /* Stores the service name of currently connected Wi-Fi
469     * network in servicename
470     */
471    FILE *command_file = NULL;
472    FILE *service_file = NULL;
473    char ln[3]         = {0};
474    char *temp;
475 
476    if (buffersize < 1)
477       return;
478 
479    temp = (char*)malloc(sizeof(char) * buffersize);
480 
481    /* Following command lists all stored services in
482     * connman settings folder, which are then used in
483     * the next while loop for parsing if the service
484     * is currently online/ready
485     */
486    snprintf(connman->command, sizeof(connman->command), "\
487          for serv in %s/wifi_*/ ; do \
488             if [ -d $serv ] ; then \
489                basename $serv ; \
490             fi ; \
491          done",
492          LAKKA_CONNMAN_DIR);
493 
494    command_file = popen(connman->command, "r");
495 
496    RARCH_LOG("[CONNMANCTL] Testing configured services for activity: command: \"%s\"\n",
497          connman->command);
498 
499    while (fgets(temp, buffersize, command_file))
500    {
501       size_t ln_size;
502       size_t temp_size = strlen(temp) - 1;
503 
504       if ((temp_size + 1) > 0)
505          if (temp[temp_size] == '\n')
506             temp[temp_size] = '\0';
507 
508       if ((temp_size + 1) == 0)
509       {
510          RARCH_WARN("[CONNMANCTL] Service name empty.\n");
511          continue;
512       }
513 
514       /* Here we test the found service for online | ready
515        * status and count the lines. Expected results are
516        * 0 = is not active, 1 = is active
517        */
518       snprintf(connman->command, sizeof(connman->command), "\
519             connmanctl services %s | \
520             grep \"^  State = \\(online\\|ready\\)\" | \
521             wc -l",
522             temp);
523 
524       service_file = popen(connman->command, "r");
525 
526       fgets(ln, sizeof(ln), service_file);
527       ln_size = strlen(ln) - 1;
528 
529       if (ln[ln_size] == '\n')
530          ln[ln_size] = '\0';
531 
532       pclose(service_file);
533 
534       RARCH_LOG("[CONNMANCTL] Service: \"%s\", status: \"%s\"\n",
535             temp, ln);
536 
537       if (ln[0] == '1')
538       {
539          pclose(command_file);
540 
541          strlcpy(servicename, temp, buffersize);
542 
543          free(temp);
544 
545          RARCH_LOG("[CONNMANCTL] Service \"%s\" considered as currently online\n",
546                servicename);
547 
548          return;
549       }
550    }
551 
552    pclose(command_file);
553 }
554 
connmanctl_tether_start_stop(void * data,bool start,char * configfile)555 static void connmanctl_tether_start_stop(void *data, bool start, char* configfile)
556 {
557    /* Start / stop wrapper for the tethering service
558     * It also checks, if we are currently connected
559     * to a wi-fi network, which needs to be disconnected
560     * before starting the tethering service, or if the
561     * tethering service is already running / not running
562     * before performing the desired action
563     */
564    FILE *command_file  = NULL;
565    char apname[64]     = {0};
566    char passkey[256]   = {0};
567    char ln[512]        = {0};
568    char ssid[64]       = {0};
569    char service[256]   = {0};
570    connman_t *connman  = (connman_t*)data;
571 #ifdef HAVE_GFX_WIDGETS
572    bool widgets_active = connman->connmanctl_widgets_supported;
573 #endif
574 
575    RARCH_LOG("[CONNMANCTL] Tether start stop: begin\n");
576 
577    if (start) /* we want to start tethering */
578    {
579       RARCH_LOG("[CONNMANCTL] Tether start stop: request to start access point\n");
580 
581       if (connmanctl_tether_status(connman)) /* check if already tethering and bail out if so */
582       {
583          RARCH_LOG("[CONNMANCTL] Tether start stop: AP already running\n");
584          runloop_msg_queue_push(msg_hash_to_str(MSG_LOCALAP_ALREADY_RUNNING),
585                1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
586                MESSAGE_QUEUE_CATEGORY_INFO);
587          return;
588       }
589 
590       /* check if there is a config file, if not, create one, if yes, parse it */
591       if (!(command_file = fopen(configfile, "r")))
592       {
593          RARCH_WARN("[CONNMANCTL] Tether start stop: config \"%s\" does not exist\n",
594                configfile);
595 
596          if (!(command_file = fopen(configfile, "w")))
597          {
598             RARCH_ERR("[CONNMANCTL] Tether start stop: cannot create config file \"%s\"\n",
599                   configfile);
600 
601             runloop_msg_queue_push(msg_hash_to_str(MSG_LOCALAP_ERROR_CONFIG_CREATE),
602                   1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
603                   MESSAGE_QUEUE_CATEGORY_ERROR);
604 
605             return;
606          }
607 
608          RARCH_LOG("[CONNMANCTL] Tether start stop: creating new config \"%s\"\n",
609                configfile);
610 
611          snprintf(apname, sizeof(apname), "LakkaAccessPoint");
612          snprintf(passkey, sizeof(passkey), "RetroArch");
613 
614          fprintf(command_file, "APNAME=%s\nPASSWORD=%s", apname, passkey);
615 
616          fclose(command_file);
617 
618          RARCH_LOG("[CONNMANCTL] Tether start stop: new config \"%s\" created\n",
619                configfile);
620       }
621       else
622       {
623          fclose(command_file);
624 
625          RARCH_LOG("[CONNMANCTL] Tether start stop: config \"%s\" exists, reading it\n",
626                configfile);
627 
628          snprintf(connman->command, sizeof(connman->command), "\
629                grep -m 1 \"^APNAME=\" %s | cut -d '=' -f 2- && \
630                grep -m 1 \"^PASSWORD=\" %s | cut -d '=' -f 2-",
631                configfile, configfile);
632 
633          command_file = popen(connman->command, "r");
634 
635          int i = 0;
636 
637          RARCH_LOG("[CONNMANCTL] Tether start stop: parsing command: \"%s\"\n",
638                connman->command);
639 
640          while (fgets(ln, sizeof(ln), command_file))
641          {
642             size_t ln_size = strlen(ln) - 1;
643 
644             i++;
645             if ((ln_size + 1) > 1)
646             {
647                if (ln[ln_size] == '\n')
648                   ln[ln_size] = '\0';
649 
650                if (i == 1)
651                {
652                   strlcpy(apname, ln, sizeof(apname));
653 
654                   RARCH_LOG("[CONNMANCTL] Tether start stop: found APNAME: \"%s\"\n",
655                         apname);
656 
657                   continue;
658                }
659 
660                if (i == 2)
661                {
662                   strlcpy(passkey, ln, sizeof(passkey));
663 
664                   RARCH_LOG("[CONNMANCTL] Tether start stop: found PASSWORD: \"%s\"\n",
665                         passkey);
666 
667                   continue;
668                }
669 
670                if (i > 2)
671                {
672                   RARCH_WARN("[CONNMANCTL] Tether start stop: you should not get here...\n");
673                   break;
674                }
675             }
676          }
677 
678          pclose(command_file);
679       }
680 
681       if (!apname || !passkey)
682       {
683          RARCH_ERR("[CONNMANCTL] Tether start stop: APNAME or PASSWORD missing\n");
684 
685          snprintf(ln, sizeof(ln),
686                msg_hash_to_str(MSG_LOCALAP_ERROR_CONFIG_PARSE),
687                configfile);
688 
689          runloop_msg_queue_push(ln,
690                1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
691                MESSAGE_QUEUE_CATEGORY_ERROR);
692 
693          return;
694       }
695 
696       /* check if connected to a wi-fi network */
697       RARCH_LOG("[CONNMANCTL] Tether start stop: checking if not connected to a wi-fi network...\n");
698       connmanctl_get_connected_ssid(connman, ssid, sizeof(ssid));
699 
700       if (strlen(ssid) != 0)
701       {
702          connmanctl_get_connected_servicename(connman, service, sizeof(service));
703 
704          if (strlen(service) != 0)
705          {
706             /* disconnect from wi-fi network */
707             RARCH_LOG("[CONNMANCTL] Tether start stop: connected to SSID \"%s\", service \"%s\"\n",
708                   ssid, service);
709 
710             snprintf(ln, sizeof(ln),
711                   msg_hash_to_str(MSG_WIFI_DISCONNECT_FROM),
712                   ssid);
713 
714             runloop_msg_queue_push(ln, 1, 180, true,
715                   NULL, MESSAGE_QUEUE_ICON_DEFAULT,
716                   MESSAGE_QUEUE_CATEGORY_INFO);
717 
718             snprintf(connman->command, sizeof(connman->command), "\
719                   connmanctl disconnect %s",
720                   service);
721 
722             command_file = popen(connman->command, "r");
723 
724             RARCH_LOG("[CONNMANCTL] Tether start stop: disconnecting from service \"%s\", command: \"%s\"\n",
725                   service, connman->command);
726 
727             while (fgets(ln, sizeof(ln), command_file))
728             {
729                size_t ln_size = strlen(ln) - 1;
730                if (ln[ln_size] == '\n')
731                   ln[ln_size] = '\0';
732 
733                RARCH_LOG("[CONNMANCTL] Tether start stop: output: \"%s\"\n",
734                      ln);
735 
736 #ifdef HAVE_GFX_WIDGETS
737                if (!widgets_active)
738 #endif
739                   runloop_msg_queue_push(ln, 1, 180, true,
740                         NULL, MESSAGE_QUEUE_ICON_DEFAULT,
741                         MESSAGE_QUEUE_CATEGORY_INFO);
742             }
743 
744             pclose(command_file);
745 
746             RARCH_LOG("[CONNMANCTL] Tether start stop: disconnect end\n");
747          }
748       }
749 
750       snprintf(connman->command, sizeof(connman->command),
751             msg_hash_to_str(MSG_LOCALAP_STARTING),
752             apname, passkey);
753 
754       runloop_msg_queue_push(connman->command,
755             1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
756             MESSAGE_QUEUE_CATEGORY_INFO);
757    }
758    else /* we want to stop tethering */
759    {
760       RARCH_LOG("[CONNMANCTL] Tether start stop: request to stop access point\n");
761 
762       if (!connmanctl_tether_status(connman)) /* check if not tethering and when not, bail out */
763       {
764          RARCH_LOG("[CONNMANCTL] Tether start stop: access point is not running\n");
765 
766          runloop_msg_queue_push(msg_hash_to_str(MSG_LOCALAP_NOT_RUNNING),
767                1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
768                MESSAGE_QUEUE_CATEGORY_INFO);
769 
770          return;
771       }
772 
773       runloop_msg_queue_push(msg_hash_to_str(MSG_LOCALAP_SWITCHING_OFF),
774             1, 180, true, NULL, MESSAGE_QUEUE_ICON_DEFAULT,
775             MESSAGE_QUEUE_CATEGORY_INFO);
776    }
777 
778    RARCH_LOG("[CONNMANCTL] Tether start stop: calling tether_toggle()\n");
779 
780    /* call the tether toggle function */
781    connmanctl_tether_toggle(connman, start, apname, passkey);
782 
783    RARCH_LOG("[CONNMANCTL] Tether start stop: end\n");
784 }
785 
786 wifi_driver_t wifi_connmanctl = {
787    connmanctl_init,
788    connmanctl_free,
789    connmanctl_start,
790    connmanctl_stop,
791    connmanctl_enable,
792    connmanctl_connection_info,
793    connmanctl_scan,
794    connmanctl_get_ssids,
795    connmanctl_ssid_is_online,
796    connmanctl_connect_ssid,
797    connmanctl_disconnect_ssid,
798    connmanctl_tether_start_stop,
799    "connmanctl",
800 };
801