#include #include #include #include /* event_struct.h must be included first cause of LIST_HEAD macro used by libevent2 */ #include #include #include #include #include /* iwinfo library (package/libiwinfo */ #include "sysinfo.h" #include "config.h" #include "list.h" //double linked list header lib from linux kernel static void parse_line(json_object *root_cur, char *line); static void parse_cfg_json_object(json_object *jso, char *buf, size_t buf_len, char **p, size_t depth, bool is_delete); static void parse_cfg_json_array(json_object *a, char *buf, size_t buf_len, char **p, size_t depth, bool is_delete); static void parse_cfg_json_string(json_object *s, char *buf, size_t buf_len, char **p, bool is_delete); static void parse_cfg_json_boolean(json_object *b, char *buf, size_t buf_len, char **p, bool is_delete); static void parse_cfg_json_int(json_object *i, char *buf, size_t buf_len, char **p, bool is_delete); static void parse_cfg_json_double(json_object *d, char *buf, size_t buf_len, char **p, bool is_delete); static void parse_cfg_json(json_object *val, char *buf, size_t buf_len, char **p, size_t depth, bool is_delete); /** * @brief check if file exists * * @param fname * @return true * @return false */ bool is_file_exists(const char *fname){ FILE *file; if ((file = fopen(fname, "r"))) { fclose(file); return true; } return false; } /** * Remove trailing white space characters from string */ size_t trim_str(char * str){ int index, i; /* Set default index */ index = -1; /* Find last index of non-white space character */ i = 0; while(str[i] != '\0') { /* somehow char 127 'del' may appear */ if(str[i] != ' ' && str[i] != '\t' && str[i] != '\n') { index = i; } i++; } /* Mark next character to last non-white space character as NULL */ str[index + 1] = '\0'; return index; } static void parse_cfg_json_object(json_object *jso, char *buf, size_t buf_len, char **p, size_t depth, bool is_delete) { lh_table *table = json_object_get_object(jso); for (struct lh_entry *entry = table->head; entry; entry = entry->next) { char subbuf[4096]; char *subp = subbuf; size_t subbuf_len = buf_len; memcpy(subbuf, buf, buf_len); subp += buf_len; char *key = (char *) entry->k; if (memcmp(key, "section_type", 12)) { const char *pattern = depth == 0 ? "%s" : ".%s"; subbuf_len += snprintf(subp, 4096, pattern, key); } parse_cfg_json((json_object *) entry->v, subbuf, subbuf_len, p, depth + 1, is_delete); } } /** * @brief parse json entry type array * * @param a * @param buf * @param buf_len * @param p * @param depth * @param is_delete */ static void parse_cfg_json_array(json_object *a, char *buf, size_t buf_len, char **p, size_t depth, bool is_delete) { array_list *list = json_object_get_array(a); size_t len = list->length; for (size_t i = 0; i < len; i++) { char subbuf[4096]; char *subp = subbuf; size_t subbuf_len = buf_len; memcpy(subbuf, buf, buf_len); subp += buf_len; subbuf_len += snprintf(subp, 512, "[%zu]", i); json_object *o = (json_object *) list->array[i]; parse_cfg_json(o, subbuf, subbuf_len, p, depth + 1, is_delete); } } /** * @brief parse json entry type string * * @param s * @param buf * @param buf_len * @param p * @param is_delete */ static void parse_cfg_json_string(json_object *s, char *buf, size_t buf_len, char **p, bool is_delete) { memcpy(*p, buf, buf_len); *p += buf_len; if (!is_delete) { const char *string = json_object_get_string(s); *p += snprintf(*p, 512, "=\"%s\"\n", string); } else { **p = '\n'; *p += 1; } } /** * @brief parse json entry type boolean * * @param b * @param buf * @param buf_len * @param p * @param is_delete * @return void* */ static void parse_cfg_json_boolean(json_object *b, char *buf, size_t buf_len, char **p, bool is_delete) { memcpy(*p, buf, buf_len); *p += buf_len; if (!is_delete) { int i = json_object_get_boolean(b); *p += snprintf(*p, 512, "=%d\n", i); } else { **p = '\n'; *p += 1; } } /** * @brief parse json entry type integer * * @param i * @param buf * @param buf_len * @param p * @param is_delete * @return char* */ static void parse_cfg_json_int(json_object *i, char *buf, size_t buf_len, char **p, bool is_delete) { memcpy(*p, buf, buf_len); *p += buf_len; if (!is_delete) { int ii = json_object_get_int(i); *p += snprintf(*p, 512, "=%d\n", ii); } else { **p = '\n'; *p += 1; } } /** * @brief parse json entry type double * * @param d * @param buf * @param buf_len * @param p * @param is_delete * @return void* */ static void parse_cfg_json_double(json_object *d, char *buf, size_t buf_len, char **p, bool is_delete) { memcpy(*p, buf, buf_len); *p += buf_len; if (!is_delete) { double dd = json_object_get_double(d); *p += snprintf(*p, 512, "=%f\n", dd); } else { **p = '\n'; *p += 1; } } /** * @brief dispatcher fucntion to parse json object * * @param val * @param buf * @param buf_len * @param p * @param depth * @param is_delete */ static void parse_cfg_json(json_object *val, char *buf, size_t buf_len, char **p, size_t depth, bool is_delete) { json_type type = json_object_get_type(val); switch (type) { case json_type_object: parse_cfg_json_object(val, buf, buf_len, p, depth, is_delete); break; case json_type_array: parse_cfg_json_array(val, buf, buf_len, p, depth, is_delete); break; case json_type_string: parse_cfg_json_string(val, buf, buf_len, p, is_delete); break; case json_type_boolean: parse_cfg_json_boolean(val, buf, buf_len, p, is_delete); break; case json_type_int: parse_cfg_json_int(val, buf, buf_len, p, is_delete); break; case json_type_double: parse_cfg_json_double(val, buf, buf_len, p, is_delete); break; case json_type_null: default: break; } } /* lh_table *table = json_object_get_object(root); for (struct lh_entry *entry = table->head; entry; entry = entry->next){ char *key = (char *)entry->k; char *val = json_object_get_string((json_object *)entry->v); syslogwda(LOG_DEBUG,"key: %s val: %s\n", key, val); } */ bool config_set_or_delete(json_object *root, bool is_delete) { syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); char *uci_command = is_delete ? "uci delete" : "uci set"; /* parse json */ char buf[16384]; /* commands buffer */ char *bp = buf; char **p = &bp; if (json_object_get_type(root) != json_type_object) { syslogwda(LOG_ALERT,"error: data is not an object\n"); return false; } parse_cfg_json(root, NULL, 0, p, 0, is_delete); syslogwda(LOG_DEBUG,"commands:\n%s\n", buf); /* exec */ FILE *fp; char command[256]; char outbuf[256]; char *line = strtok(buf, "\n"); do { snprintf(command, 256, "%s %s", uci_command, line); syslogwda(LOG_WARNING,"cmd exec: %s\n", command); fp = popen(command, "r"); if (fp == NULL) { syslogwda(LOG_ALERT,"error: Unable to open process %s\n", command); return false; } /* print cmd outbuf */ while (fgets(outbuf, 256, fp) != NULL) { syslogwda(LOG_DEBUG,"%s\n", outbuf); } /* status code */ int ret = WEXITSTATUS(pclose(fp)); if (ret != 0) syslogwda(LOG_WARNING,"status code: %d %s\n", ret, strerror(ret)); } while ((line = strtok(NULL, "\n"))); /* commit and reload */ snprintf(buf, 512, "uci commit\nreload_config\n"); line = strtok(buf, "\n"); do { snprintf(command, 256, "%s", line); syslogwda(LOG_WARNING,"cmd exec: %s\n", command); fp = popen(command, "r"); if (fp == NULL) { syslogwda(LOG_ALERT,"error: %s Unable to open process %s\n", strerror(errno), command); return false; } /* print cmd outbuf */ while (fgets(outbuf, 256, fp) != NULL) { syslogwda(LOG_DEBUG,"%s\n", outbuf); } /* status code */ int ret = WEXITSTATUS(pclose(fp)); if(ret != 0) syslogwda(LOG_DEBUG,"status code: %d %s\n", ret, strerror(ret)); } while ((line = strtok(NULL, "\n"))); return true; } size_t read_file(const char *filepath, char *buf, size_t buf_len){ syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); FILE *fp = fopen(filepath,"r"); if(fp == NULL){ syslogwda(LOG_ALERT,"error: Unable to open file %s\n", filepath); return 0; } size_t read = fread(buf, 1, buf_len, fp); buf[read] = '\0'; //set terminator fclose(fp); return read; } /** * @brief exec given command with shell * * @param command * @param buf * @param buf_len * @return int */ int exec(const char *command, char *buf, size_t buf_len){ syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); FILE *fp; syslogwda(LOG_NOTICE,"exec: %s\n", command); fp = popen(command, "r"); if (fp == NULL) { syslogwda(LOG_ERR,"error: %s Unable to open process %s\n", strerror(errno), command); return errno; } if(buf == NULL || buf_len == 0) { buf_len = 8192; char tmp_buf[buf_len]; buf = (char *)&tmp_buf; } size_t p = fread(buf, 1, buf_len, fp); //fread will return number of 1 byte entries readed /* check bounds */ if(p > buf_len){ syslogwda(LOG_ERR, "INFO: exec stdout > buf_len %zu\n", buf_len); buf[buf_len - 1] = '\0'; } else { buf[p] = '\0'; //set terminator } /* status code */ int ret = WEXITSTATUS(pclose(fp)); if(ret != 0) syslogwda(LOG_NOTICE, "status code: %d %s\n", ret, strerror(ret)); return ret; } /** * @brief exec given command with format like printf, fprintf etc * * @param buf * @param buflen * @param fmt * @param ... * @return int */ int execf(char *buf, size_t buflen, const char *fmt, ...){ size_t cmdlen = 2048; char cmd[cmdlen]; va_list arg; va_start (arg, fmt); cmdlen -= vsnprintf(cmd, cmdlen, fmt, arg); va_end (arg); return exec(cmd, buf, buflen); } int exec2(char *outbuf, size_t *outbufsize, char *errbuf, size_t *errbufsize, char *argv[]) { int fdout[2]; /* pipe fd array for the stdout */ int fderr[2]; /* pipe fd array for the stderr */ pid_t pid; /* process id */ int status; /* place to store child process status */ if (pipe(fdout)==-1) /* first pipe for the stdout */ perror("pipe out"); /* exit with error if something wrong */ if (pipe(fderr)==-1) /* second pipe for the stderr */ perror("pipe err"); /* * On success, the PID of the child process is returned in the * parent, and 0 is returned in the child. On failure, -1 is * returned in the parent, no child process is created, and errno is * set to indicate the error. */ if ((pid = fork()) == -1) /* fork current process and store pid */ perror("fork error"); if(pid == 0) { /* if pid == 0 than this is a child process */ dup2 (fdout[1], STDOUT_FILENO); /* send stdout to the pipe fdout */ dup2 (fderr[1], STDERR_FILENO); /* send stderr to the pipe fderr */ /* close both sides of the pipe in the child */ close(fdout[0]); close(fdout[1]); /* close both sides of the pipe in the child */ close(fderr[0]); close(fderr[1]); /* execvp is called in the child ps * argv[0] is a ptr to the first arg * &argv[0] is a ptr to a ptr to the first arg */ execvp(argv[0], &argv[0]); /* this part is never reached if execvp success */ return errno; } else { /* parent */ while(wait(&status) > 0){} /* wait for the child processes to finish */ /* close write ends of the pipes */ close(fdout[1]); close(fderr[1]); /* if child process finished with errorprint error * and return error code from parent process */ if (WIFEXITED(status) && WEXITSTATUS(status)){ printf("%s\n", strerror(WEXITSTATUS(status))); return WEXITSTATUS(status); } *outbufsize = read(fdout[0], outbuf, *outbufsize); outbuf[*outbufsize] = '\0'; //printf("OUT: \"%.*s\"\n", (int)*outbufsize, outbuf); *errbufsize = read(fderr[0], errbuf, *errbufsize); errbuf[*errbufsize] = '\0'; //printf("ERR: \"%.*s\"\n", (int)*errbufsize, errbuf); /* close read ends of the pipes */ close(fdout[0]); close(fderr[0]); } return 0; } json_object *config_get_raw() { syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); size_t bufsize = 32768; char *buf = malloc(bufsize); exec("uci show", buf, bufsize); json_object *json_array = json_object_new_array(); char *tok = strtok(buf, "\n"); size_t idx = 0; do { syslogwda(LOG_INFO,"uci cmd: %s\n", tok); json_object *json_string = json_object_new_string(tok); json_object_array_put_idx(json_array, idx, json_string); idx++; } while ((tok = strtok(NULL, "\n"))); free(buf); return json_array; } void config_wireless_iface(bool remove){ syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); char buf[16384]; exec("uci show wireless | grep '=wifi-iface'", buf, 16384); char *tok = strtok(buf, "\n"); if(tok == NULL) return; size_t idx = 0; do { char *last = strchr(tok,'='); if(last == NULL){ syslogwda(LOG_INFO,"%zu found: '%s' wifi-iface not found! skip.\n", idx, tok); continue; } size_t len = last - tok; char wifi_iface[len + 1]; /* + 1 for terminator byte '\0' */ memcpy(wifi_iface, tok, len); wifi_iface[len] = '\0'; /* terminate string */ syslogwda(LOG_INFO,"%zu found '%s' len: %zu wifi-iface: '%s'\n", idx, tok, len, wifi_iface); if(remove) { char command[256]; snprintf(command, 256, "uci delete %s", wifi_iface); exec(command, NULL, 0); } idx++; } while ((tok = strtok(NULL, "\n"))); return; } void config_network_clear(){ syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); char buf[16384]; exec("uci show network | grep -E '=[device|interface]' | grep -Ev 'dev\\d{1,2}=|loopback'", buf, 16384); char *tok = strtok(buf, "\n"); if(tok == NULL) return; size_t idx = 0; do { char *last = strchr(tok,'='); if(last == NULL){ syslogwda(LOG_DEBUG,"%zu found: '%s' network interface not found! skip.\n", idx, tok); continue; } size_t len = last - tok; char net_iface[len + 1]; /* + 1 for terminator byte '\0' */ memcpy(net_iface, tok, len); net_iface[len] = '\0'; /* terminate string */ syslogwda(LOG_DEBUG,"%zu found '%s' len: %zu network iface: '%s'\n", idx, tok, len, net_iface); char command[256]; snprintf(command, 256, "uci delete %s", net_iface); exec(command, NULL, 0); idx++; } while ((tok = strtok(NULL, "\n"))); return; } json_object *config_get() { syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); FILE *fp; char string[1024]; /* stdout string len */ fp = popen("uci show", "r"); if (fp == NULL) return false; /* json init */ json_object *root = json_object_new_object(); while (fgets(string, 1024, fp) != NULL) { parse_line(root, string); } pclose(fp); return root; } static void parse_line(json_object *root_cur, char *line) { char *start = NULL, *end = NULL, *pos = NULL, *val = NULL, *tok = NULL, *tok_prev = NULL, *tok_last = NULL; json_object *root_parent = NULL, *child = NULL; bool is_section_type; //this flag for special parse uci section_type e.g.: wireless.2g=wifi-iface is_section_type = false; /* value delimiter */ char delim = '='; pos = strchr(line, delim); if (pos == NULL) { syslogwda(LOG_DEBUG,"warning: delimiter '%c' not found in the line: %s\n", delim, line); return; } *pos = '\0'; /* set terminator inplace delimiter */ val = pos + 1; /* remove trailing \n */ size_t len = strlen(val); end = val + len - 1; // last char pos if (*end == '\n') *end = '\0'; end--; // replace \n with line terminator /* remove quotes if exists */ if (*val == '"' || *val == '\'') { val++; //shift forward to remove opening quote if (*end == '"' || *end == '\'') *end = '\0'; end--; // shift backward to remove closing quote } else{ is_section_type = true; } /*last token detection */ tok_last = strrchr(line, '.'); if (tok_last) { tok_last += 1; } else { tok_last = line; }; /* split line by dots */ tok = strtok(line, "."); do { size_t idx = 0; bool is_array = false; json_object *container; //syslogwda(LOG_DEBUG,"\ttok: %s \n", tok); /* if array is present in token */ if ((start = strchr(tok, '['))) { *start = '\0'; /* set terminator char */ //syslogwda(LOG_DEBUG,"\ttok clean: %s\n", tok); if ((end = strchr(start + 1, ']'))) { is_array = true; *end = '\0'; /* set terminator char */ idx = atoi(start + 1); /* array index */ //syslogwda(LOG_DEBUG,"\t\tarray idx: %d\n", idx); } } /* if last token */ if (tok == tok_last) { child = json_object_new_string(val); /* 2 scenarios */ if (!is_array) { if (json_object_get_type(root_cur) != json_type_object){ json_object_object_del(root_parent, tok_prev); root_cur = json_object_new_object(); json_object_object_add(root_parent, tok_prev, root_cur); } //special parse section_type if(is_section_type){ json_object *jso = json_object_new_object(); json_object_object_add(root_cur, tok, jso); root_cur = jso; json_object_object_add(root_cur, "section_type", child); }else { json_object_object_add(root_cur, tok, child); } } else if (is_array) { //special parse section_type if(is_section_type){ json_object *jso = json_object_new_object(); json_object_object_add(jso, "section_type", child); child = jso; } json_object_object_get_ex(root_cur, tok, &container); //remove if not an array if(json_object_get_type(container) != json_type_array){ json_object_object_del(root_cur, tok); //delete container container = json_object_new_array(); json_object_object_add(root_cur, tok, container); } json_object_array_put_idx(container, idx, child); } continue; } /* get tok container */ json_object_object_get_ex(root_cur, tok, &container); //save for next cycles root_cur and tok root_parent = root_cur; tok_prev = tok; /* 4 scenarios */ if (!is_array && container) { root_cur = container; } else if (!is_array && !container) { child = json_object_new_object(); json_object_object_add(root_cur, tok, child); root_cur = child; } else if (is_array && container) { if(json_object_get_type(container) != json_type_array){ json_object_object_del(root_cur, tok); //delete container container = json_object_new_array(); json_object_object_add(root_cur, tok, container); child = NULL; } else { child = json_object_array_get_idx(container, idx); } if (!child) { child = json_object_new_object(); json_object_array_put_idx(container, idx, child); } root_cur = child; } else if (is_array && !container) { child = json_object_new_object(); container = json_object_new_array(); json_object_array_put_idx(container, idx, child); json_object_object_add(root_cur, tok, container); root_cur = child; } } while ((tok = strtok(NULL, "."))); } int wireless_del_station(const char *dev, const char *mac) { syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); syslogwda(LOG_WARNING,"delete sta dev: %s mac: %s\n", dev, mac); //if dev and mac passed if(dev && mac){ return ubus_hostpad_del_station(dev, mac); } //if dev passed and mac is not size_t entry_size = sizeof(struct iwinfo_assoclist_entry); const struct iwinfo_ops *iw; int len = 0; char buf[IWINFO_BUFSIZE]; if(dev && !mac) { iw = iwinfo_backend(dev); iw->assoclist(dev, buf, &len); for (int i = 0; i < len; i += entry_size) { struct iwinfo_assoclist_entry *e; e = (struct iwinfo_assoclist_entry *) &buf[i]; int ret = ubus_hostpad_del_station(dev, format_mac((uint8_t *)e->mac)); if(ret != 0) return ret; } return 0; } //if dev is not passed /* get wlan devs */ size_t devs_size = 50; net_dev netdev[devs_size]; net_devs devs = {.cnt = -1, .dev = (net_dev *)&netdev}; get_wifi_dev(&devs, devs_size); for (int i = 0; i < devs.cnt; i++) { char *ifname = devs.dev[i].name; /* get assoc. users list */ iw = iwinfo_backend(ifname); iw->assoclist(ifname, buf, &len); for (int i = 0; i < len; i += entry_size) { struct iwinfo_assoclist_entry *e; e = (struct iwinfo_assoclist_entry *) &buf[i]; int ret = ubus_hostpad_del_station(ifname, format_mac((uint8_t *)e->mac)); if(ret != 0) return ret; } } return 0; } int ubus_hostpad_del_station(const char *dev, const char *mac) { syslogwda(LOG_INFO,"%s() %s:%d\n", __func__, __FILE__, __LINE__); syslogwda(LOG_WARNING,"delete sta dev: %s mac: %s\n", dev, mac); if(!dev || !mac) return -1; char command[256]; snprintf(command, 256, "ubus call hostapd.%s " "del_client " "\"{'addr':'%s', 'reason':5, 'deauth':true, 'ban_time':0}\"", dev, mac); FILE *fp = popen(command, "r"); /* status code */ int ret = WEXITSTATUS(pclose(fp)); if(ret != 0) syslogwda(LOG_WARNING,"status code: %d %s\n", ret, strerror(ret)); return ret; }