/* * Copyright (C) 2022-2025 ArtInChip Technology Co., Ltd. * * SPDX-License-Identifier: Apache-2.0 * * Authors: lv.wu */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wifimanager.h" #define LIST_NETWORK_MAX 4096 #define USER_SOCKET "/var/run/user_cb_socket" struct cb_info { int evt; int buff_length; }; static pthread_t gp_user_callback; static int g_server_fd = -1; static int g_cli_fd = -1; static const char *wifistate2string(wifistate_t state) { switch (state) { case WIFI_STATE_GOT_IP: return "WIFI_STATE_GOT_IP"; case WIFI_STATE_CONNECTING: return "WIFI_STATE_CONNECTING"; case WIFI_STATE_DHCPC_REQUEST: return "WIFI_STATE_DHCPC_REQUEST"; case WIFI_STATE_DISCONNECTED: return "WIFI_STATE_DISCONNECTED"; case WIFI_STATE_CONNECTED: return "WIFI_STATE_CONNECTED"; default: return "WIFI_STATE_ERROR"; } } static const char *disconn_reason2string(wifimanager_disconn_reason_t reason) { switch (reason) { case AUTO_DISCONNECT: return "wpa auto disconnect"; case ACTIVE_DISCONNECT: return "active disconnect"; case KEYMT_NO_SUPPORT: return "keymt is not supported"; case CMD_OR_PARAMS_ERROR: return "wpas command error"; case IS_CONNECTTING: return "wifi is still connecting"; case CONNECT_TIMEOUT: return "connect timeout"; case REQUEST_IP_TIMEOUT: return "request ip address timeout"; case WPA_TERMINATING: return "wpa_supplicant is closed"; case AP_ASSOC_REJECT: return "AP assoc reject"; case NETWORK_NOT_FOUND: return "can't search such ssid"; case PASSWORD_INCORRECT: return "incorrect password"; default: return "other reason"; } } /* This callback is running in wifimanager daemon process, * So you can't process other logic in this callback, such as UI display... * * It is recommended to use inter-process communication (IPC) to send the * results of the WIFIMANAGER DAEMON to the process that needs to execute them. */ static void print_scan_result(char *result) { int fd; struct sockaddr_un saddr = { .sun_family = AF_UNIX }; struct cb_info info; snprintf(saddr.sun_path, sizeof(saddr.sun_path) - 1, USER_SOCKET); if ((fd = socket(PF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0)) == -1) { printf("client: socket alloc error\n"); return; } if (connect(fd, (struct sockaddr *)(&saddr), sizeof(saddr)) == -1) { printf("client: connect error\n"); close(fd); return; } info.evt = 0; info.buff_length = strlen(result); send(fd, &info, sizeof(struct cb_info), 0); send(fd, result, strlen(result), 0); close(fd); } /* This callback is running in wifimanager daemon process, * So you can't process other logic in this callback, such as UI display... * * It is recommended to use inter-process communication (IPC) to send the * results of the WIFIMANAGER DAEMON to the process that needs to execute them. */ static void print_stat_change(wifistate_t stat, wifimanager_disconn_reason_t reason) { int fd; struct sockaddr_un saddr = { .sun_family = AF_UNIX }; struct cb_info info; int buff[2]; snprintf(saddr.sun_path, sizeof(saddr.sun_path) - 1, USER_SOCKET); if ((fd = socket(PF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0)) == -1) printf("client: socket alloc error\n"); return; if (connect(fd, (struct sockaddr *)(&saddr), sizeof(saddr)) == -1) { printf("client: connect error\n"); close(fd); return; } info.evt = 1; info.buff_length = sizeof(int) * 2; buff[0] = stat; buff[1] = reason; send(fd, &info, sizeof(struct cb_info),0); send(fd, buff, sizeof(int) * 2,0); close(fd); } static void *user_callback_thread(void *arg) { struct sockaddr_un saddr = { .sun_family = AF_UNIX }; snprintf(saddr.sun_path, sizeof(saddr.sun_path) - 1, USER_SOCKET); int header[2]; void *buff; g_server_fd = socket(PF_UNIX, SOCK_SEQPACKET, 0); if (g_server_fd == -1) { printf("server: socket alloc error\n"); return NULL; } if (bind(g_server_fd, (struct sockaddr *)(&saddr), sizeof(saddr)) == -1) { printf("server: bind error\n"); goto fail; } if (chmod(saddr.sun_path, 0660) == -1) { printf("server: chmod error\n"); goto fail; } if (listen(g_server_fd, 2) == -1) { printf("server: listen error\n"); goto fail; } while (1) { int len; g_cli_fd = accept(g_server_fd, NULL, NULL); len = recv(g_cli_fd, header, sizeof(int) * 2, 0); if (len <= 0) { close(g_cli_fd); g_cli_fd = -1; continue; } buff = malloc(header[1] + 1); if (len <= 0) { close(g_cli_fd); g_cli_fd = -1; continue; } len = recv(g_cli_fd, buff, header[1], 0); if (len <= 0) { free(buff); close(g_cli_fd); g_cli_fd = -1; continue; } if (header[0] == 0) { printf("\n-------------------------scan result-------------------------\n"); printf("%s\n", buff); printf("---------------------------------------------------------------\n"); } else if (header[0] == 1) { printf("\n-------------------connection change result------------------\n"); printf("%s\n", wifistate2string(((int *)buff)[0])); if (((int *)buff)[0] == WIFI_STATE_DISCONNECTED) printf("disconnect reason: %s\n", disconn_reason2string(((int *)buff)[1])); printf("---------------------------------------------------------------\n"); } free(buff); close(g_cli_fd); g_cli_fd = -1; } fail: close(g_server_fd); g_server_fd = -1; pthread_exit(NULL); } /* release socket resource */ static void loop_stop(int sig) { struct sigaction sigact = { .sa_handler = SIG_DFL }; sigaction(sig, &sigact, NULL); pthread_cancel(gp_user_callback); pthread_join(gp_user_callback, NULL); if (g_server_fd != -1) close(g_server_fd); if (g_cli_fd != -1) close(g_cli_fd); unlink(USER_SOCKET); } static int wait_loop() { pid_t pid; if((pid = fork()) < 0) return 0; else if(pid) return 0; pthread_create(&gp_user_callback, NULL, user_callback_thread, NULL); struct sigaction sigact = { .sa_handler = SIG_IGN }; sigaction(SIGPIPE, &sigact, NULL); /* free ctl resource */ sigact.sa_handler = loop_stop; sigaction(SIGTERM, &sigact, NULL); sigaction(SIGINT, &sigact, NULL); while(1) { sleep(1); } return 0; } int main(int argc, char *argv[]) { int opt; const char *opts = "c:hslriod"; char list_net_results[LIST_NETWORK_MAX]; wifi_status_t status = {0}; int ret = -1; struct option longopts[] = { {"connect", required_argument, NULL, 'c'}, {"scan", no_argument, NULL, 's'}, {"list_network", no_argument, NULL, 'l'}, {"remove_network", no_argument, NULL, 'r'}, {"status", no_argument, NULL, 'i'}, {"help", no_argument, NULL, 'h'}, {"open", no_argument, NULL, 'o'}, {"close", no_argument, NULL, 'd'}, { 0, 0, 0, 0 }, }; wifimanager_cb_t cb = { .scan_result_cb = print_scan_result, .stat_change_cb = print_stat_change, }; if(argc == 1) goto usage; while ((opt = getopt_long(argc, argv, opts, longopts, NULL)) != -1) { switch(opt) { case 'c': if(argc > 5 || argc < 3) { printf(" -c, --connect\t\tconnect AP, -c [password]\n"); } else if (argc == 3) { /* Open */ printf("connecting open wifi [ssid:%s] \n", argv[2]); wifimanager_connect(argv[2], NULL); } else { printf("connecting ssid:%s passward:%s\n", argv[optind-1], argv[optind]); wifimanager_connect(argv[optind-1], argv[optind]); } return EXIT_SUCCESS; case 'h': usage: printf(" -h, --help\t\tprint this help\n"); printf(" -c, --connect\t\tconnect AP, -c [password]\n"); printf(" -s, --scan\t\tscan AP\n"); printf(" -l, --list\tlist all networks\n"); printf(" -i, --status\t\tget wifi status information\n"); printf(" -r, --remove\tremove network in config, -r \n"); printf(" -o, --open\topen WifiManager\n"); printf(" -d, --close\tclose WifiManager\n"); return EXIT_SUCCESS; case 's': ret = wifimanager_scan(); return EXIT_SUCCESS; case 'l': ret = wifimanager_list_networks(list_net_results, LIST_NETWORK_MAX); if(ret != -1) { printf("%s\n", list_net_results); } return EXIT_SUCCESS; case 'i': ret = wifimanager_get_status(&status); if(ret >= 0) { printf("wifi state: %s\n", wifistate2string(status.state)); if((status.state == WIFI_STATE_GOT_IP) || (status.state == WIFI_STATE_CONNECTED) || (status.state == WIFI_STATE_DHCPC_REQUEST)) { printf("ssid:%s\n", status.ssid); printf("bssid:%s\n", status.bssid); printf("key mgmt: %s\n", status.key_mgmt); printf("current frequency: %d GHz\n", status.freq); printf("ip address: %s\n", status.ip_address); printf("mac address: %s\n", status.mac_address); printf("rssi: %d\n", status.rssi); printf("link speed: %d\n", status.link_speed); printf("noise: %d\n", status.noise); } } return EXIT_SUCCESS; case 'r': if(argc < 2) { printf(" -r, --remove_net\tremove network in config, -r \n"); return EXIT_SUCCESS; } printf("removing ssid:%s\n", argv[optind]); wifimanager_remove_networks(argv[optind], strlen(argv[optind])); return EXIT_SUCCESS; case 'o': if (wifimanager_init(&cb)) { printf("wifimanager init failed\n"); return -1; } printf("wifimanager init ok\n"); /* create a child process, in case user_callback_thread don't be closed * just for this test. if your farther process won't exit, ignore it */ wait_loop(); return EXIT_SUCCESS; case 'd': wifimanager_deinit(); printf("wifimanager deinit ok\n"); return EXIT_SUCCESS; default: printf("Try '%s -h' for more information.\n", argv[0]); return EXIT_FAILURE; } if (optind == argc) goto usage; } }