SoftapController.cpp \
TetherController.cpp \
oem_iptables_hook.cpp \
- logwrapper.c \
main.cpp \
#include <errno.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LOG_TAG "IdletimerController"
#include <cutils/log.h>
+#include <logwrap/logwrap.h>
#include "IdletimerController.h"
#include "NetdConstants.h"
-extern "C" int system_nosh(const char *command);
-
const char* IdletimerController::LOCAL_RAW_PREROUTING = "idletimer_raw_PREROUTING";
const char* IdletimerController::LOCAL_MANGLE_POSTROUTING = "idletimer_mangle_POSTROUTING";
IdletimerController::~IdletimerController() {
}
/* return 0 or non-zero */
-int IdletimerController::runIpxtablesCmd(const char *cmd) {
- char *buffer;
- size_t len = strnlen(cmd, 255);
+int IdletimerController::runIpxtablesCmd(int argc, const char **argv) {
int res;
- if (len == 255) {
- ALOGE("command too long");
- return -1;
- }
-
- asprintf(&buffer, "%s %s", IPTABLES_PATH, cmd);
- res = system_nosh(buffer);
- ALOGV("%s #%d", buffer, res);
- free(buffer);
-
+ res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
+ ALOGV("runCmd() res=%d", res);
return res;
}
int IdletimerController::setDefaults() {
int res;
- char *buffer;
- asprintf(&buffer, "-t raw -F %s", LOCAL_RAW_PREROUTING);
- res = runIpxtablesCmd(buffer);
- free(buffer);
+ const char *cmd1[] = {
+ IPTABLES_PATH,
+ "-t",
+ "raw",
+ "-F",
+ LOCAL_RAW_PREROUTING
+ };
+ res = runIpxtablesCmd(ARRAY_SIZE(cmd1), cmd1);
if (res)
return res;
- asprintf(&buffer, "-t mangle -F %s", LOCAL_MANGLE_POSTROUTING);
- res = runIpxtablesCmd(buffer);
- free(buffer);
+ const char *cmd2[] = {
+ IPTABLES_PATH,
+ "-t",
+ "mangle",
+ "-F",
+ LOCAL_MANGLE_POSTROUTING
+ };
+ res = runIpxtablesCmd(ARRAY_SIZE(cmd2), cmd2);
+
return res;
}
uint32_t timeout,
const char *classLabel) {
int res;
- char *buffer;
- asprintf(&buffer, "-t raw -%c %s -i %s -j IDLETIMER"
- " --timeout %u --label %s --send_nl_msg 1",
- (op == IptOpAdd) ? 'A' : 'D', LOCAL_RAW_PREROUTING, iface, timeout, classLabel);
- res = runIpxtablesCmd(buffer);
- free(buffer);
+ char timeout_str[11]; //enough to store any 32-bit unsigned decimal
+
+ snprintf(timeout_str, sizeof(timeout_str), "%u", timeout);
+
+ const char *cmd1[] = {
+ IPTABLES_PATH,
+ "-t",
+ "raw",
+ (op == IptOpAdd) ? "-A" : "-D",
+ LOCAL_RAW_PREROUTING,
+ "-i",
+ iface,
+ "-j",
+ "IDLETIMER",
+ "--timeout",
+ timeout_str,
+ "--label",
+ classLabel,
+ "--send_nl_msg",
+ "1"
+ };
+ res = runIpxtablesCmd(ARRAY_SIZE(cmd1), cmd1);
if (res)
return res;
- asprintf(&buffer, "-t mangle -%c %s -o %s -j IDLETIMER"
- " --timeout %u --label %s --send_nl_msg 1",
- (op == IptOpAdd) ? 'A' : 'D', LOCAL_MANGLE_POSTROUTING, iface, timeout, classLabel);
- res = runIpxtablesCmd(buffer);
- free(buffer);
+ const char *cmd2[] = {
+ IPTABLES_PATH,
+ "-t",
+ "mangle",
+ (op == IptOpAdd) ? "-A" : "-D",
+ LOCAL_MANGLE_POSTROUTING,
+ "-o",
+ iface,
+ "-j",
+ "IDLETIMER",
+ "--timeout",
+ timeout_str,
+ "--label",
+ classLabel,
+ "--send_nl_msg",
+ "1"
+ };
+ res = runIpxtablesCmd(ARRAY_SIZE(cmd2), cmd2);
return res;
}
private:
enum IptOp { IptOpAdd, IptOpDelete };
int setDefaults();
- int runIpxtablesCmd(const char *cmd);
+ int runIpxtablesCmd(int argc, const char **cmd);
int modifyInterfaceIdletimer(IptOp op, const char *iface, uint32_t timeout,
const char *classLabel);
};
#include <errno.h>
#include <sys/socket.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define LOG_TAG "NatController"
#include <cutils/log.h>
+#include <logwrap/logwrap.h>
#include "NatController.h"
#include "SecondaryTableController.h"
#include "NetdConstants.h"
-extern "C" int system_nosh(const char *command);
-
const char* NatController::LOCAL_FORWARD = "natctrl_FORWARD";
const char* NatController::LOCAL_NAT_POSTROUTING = "natctrl_nat_POSTROUTING";
NatController::~NatController() {
}
-int NatController::runCmd(const char *path, const char *cmd) {
- char *buffer;
- size_t len = strnlen(cmd, 255);
+int NatController::runCmd(int argc, const char **argv) {
int res;
- if (len == 255) {
- ALOGE("command too long");
- errno = E2BIG;
- return -1;
- }
-
- asprintf(&buffer, "%s %s", path, cmd);
- res = system_nosh(buffer);
- ALOGV("runCmd() buffer='%s' res=%d", buffer, res);
- free(buffer);
+ res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
+ ALOGV("runCmd() res=%d", res);
return res;
}
}
int NatController::setDefaults() {
- if (runCmd(IPTABLES_PATH, "-F natctrl_FORWARD"))
+ const char *cmd1[] = {
+ IPTABLES_PATH,
+ "-F",
+ "natctrl_FORWARD"
+ };
+ if (runCmd(ARRAY_SIZE(cmd1), cmd1))
return -1;
- if (runCmd(IPTABLES_PATH, "-t nat -F natctrl_nat_POSTROUTING"))
+
+ const char *cmd2[] = {
+ IPTABLES_PATH,
+ "-t",
+ "nat",
+ "-F",
+ "natctrl_nat_POSTROUTING"
+ };
+ if (runCmd(ARRAY_SIZE(cmd2), cmd2))
return -1;
- runCmd(IP_PATH, "rule flush");
- runCmd(IP_PATH, "-6 rule flush");
- runCmd(IP_PATH, "rule add from all lookup default prio 32767");
- runCmd(IP_PATH, "rule add from all lookup main prio 32766");
- runCmd(IP_PATH, "-6 rule add from all lookup default prio 32767");
- runCmd(IP_PATH, "-6 rule add from all lookup main prio 32766");
- runCmd(IP_PATH, "route flush cache");
+ const char *cmd3[] = {
+ IP_PATH,
+ "rule",
+ "flush"
+ };
+ runCmd(ARRAY_SIZE(cmd3), cmd3);
+
+ const char *cmd4[] = {
+ IP_PATH,
+ "-6",
+ "rule",
+ "flush"
+ };
+ runCmd(ARRAY_SIZE(cmd4), cmd4);
+
+ const char *cmd5[] = {
+ IP_PATH,
+ "rule",
+ "add",
+ "from",
+ "all",
+ "lookup",
+ "default",
+ "prio",
+ "32767"
+ };
+ runCmd(ARRAY_SIZE(cmd5), cmd5);
+
+ const char *cmd6[] = {
+ IP_PATH,
+ "rule",
+ "add",
+ "from",
+ "all",
+ "lookup",
+ "main",
+ "prio",
+ "32766"
+ };
+ runCmd(ARRAY_SIZE(cmd6), cmd6);
+
+ const char *cmd7[] = {
+ IP_PATH,
+ "-6",
+ "rule",
+ "add",
+ "from",
+ "all",
+ "lookup",
+ "default",
+ "prio",
+ "32767"
+ };
+ runCmd(ARRAY_SIZE(cmd7), cmd7);
+
+ const char *cmd8[] = {
+ IP_PATH,
+ "-6",
+ "rule",
+ "add",
+ "from",
+ "all",
+ "lookup",
+ "main",
+ "prio",
+ "32766"
+ };
+ runCmd(ARRAY_SIZE(cmd8), cmd8);
+
+ const char *cmd9[] = {
+ IP_PATH,
+ "route",
+ "flush",
+ "cache"
+ };
+ runCmd(ARRAY_SIZE(cmd9), cmd9);
natCount = 0;
// 0 1 2 3 4 5
// nat enable intface extface addrcnt nated-ipaddr/prelength
int NatController::enableNat(const int argc, char **argv) {
- char cmd[255];
int i;
int addrCount = atoi(argv[4]);
int ret = 0;
ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
}
- runCmd(IP_PATH, "route flush cache");
+ const char *cmd[] = {
+ IP_PATH,
+ "route",
+ "flush",
+ "cache"
+ };
+ runCmd(ARRAY_SIZE(cmd), cmd);
}
if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
}
- runCmd(IP_PATH, "route flush cache");
+ const char *cmd[] = {
+ IP_PATH,
+ "route",
+ "flush",
+ "cache"
+ };
+ runCmd(ARRAY_SIZE(cmd), cmd);
}
ALOGE("Error setting forward rules");
errno = ENODEV;
}
/* Always make sure the drop rule is at the end */
- snprintf(cmd, sizeof(cmd), "-D natctrl_FORWARD -j DROP");
- runCmd(IPTABLES_PATH, cmd);
- snprintf(cmd, sizeof(cmd), "-A natctrl_FORWARD -j DROP");
- runCmd(IPTABLES_PATH, cmd);
+ const char *cmd1[] = {
+ IPTABLES_PATH,
+ "-D",
+ "natctrl_FORWARD",
+ "-j",
+ "DROP"
+ };
+ runCmd(ARRAY_SIZE(cmd1), cmd1);
+ const char *cmd2[] = {
+ IPTABLES_PATH,
+ "-A",
+ "natctrl_FORWARD",
+ "-j",
+ "DROP"
+ };
+ runCmd(ARRAY_SIZE(cmd2), cmd2);
natCount++;
// add this if we are the first added nat
if (natCount == 1) {
- snprintf(cmd, sizeof(cmd), "-t nat -A natctrl_nat_POSTROUTING -o %s -j MASQUERADE", extIface);
- if (runCmd(IPTABLES_PATH, cmd)) {
- ALOGE("Error seting postroute rule: %s", cmd);
+ const char *cmd[] = {
+ IPTABLES_PATH,
+ "-t",
+ "nat",
+ "-A",
+ "natctrl_nat_POSTROUTING",
+ "-o",
+ extIface,
+ "-j",
+ "MASQUERADE"
+ };
+ if (runCmd(ARRAY_SIZE(cmd), cmd)) {
+ ALOGE("Error seting postroute rule: iface=%s", extIface);
// unwind what's been done, but don't care about success - what more could we do?
for (i = 0; i < addrCount; i++) {
secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
}
int NatController::setForwardRules(bool add, const char *intIface, const char * extIface) {
- char cmd[255];
-
- snprintf(cmd, sizeof(cmd),
- "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
- (add ? "A" : "D"),
- extIface, intIface);
- if (runCmd(IPTABLES_PATH, cmd) && add) {
+ const char *cmd1[] = {
+ IPTABLES_PATH,
+ add ? "-A" : "-D",
+ "natctrl_FORWARD",
+ "-i",
+ extIface,
+ "-o",
+ intIface,
+ "-m",
+ "state",
+ "--state",
+ "ESTABLISHED,RELATED",
+ "-j",
+ "RETURN"
+ };
+ int rc = 0;
+
+ if (runCmd(ARRAY_SIZE(cmd1), cmd1) && add) {
return -1;
}
- snprintf(cmd, sizeof(cmd),
- "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
- (add ? "A" : "D"),
- intIface, extIface);
- if (runCmd(IPTABLES_PATH, cmd) && add) {
+ const char *cmd2[] = {
+ IPTABLES_PATH,
+ add ? "-A" : "-D",
+ "natctrl_FORWARD",
+ "-i",
+ intIface,
+ "-o",
+ extIface,
+ "-m",
+ "state",
+ "--state",
+ "INVALID",
+ "-j",
+ "DROP"
+ };
+
+ const char *cmd3[] = {
+ IPTABLES_PATH,
+ add ? "-A" : "-D",
+ "natctrl_FORWARD",
+ "-i",
+ intIface,
+ "-o",
+ extIface,
+ "-j",
+ "RETURN"
+ };
+
+ if (runCmd(ARRAY_SIZE(cmd2), cmd2) && add) {
// bail on error, but only if adding
- snprintf(cmd, sizeof(cmd),
- "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
- (!add ? "A" : "D"),
- extIface, intIface);
- runCmd(IPTABLES_PATH, cmd);
- return -1;
+ rc = -1;
+ goto err_invalid_drop;
}
- snprintf(cmd, sizeof(cmd), "-%s natctrl_FORWARD -i %s -o %s -j RETURN", (add ? "A" : "D"),
- intIface, extIface);
- if (runCmd(IPTABLES_PATH, cmd) && add) {
+ if (runCmd(ARRAY_SIZE(cmd3), cmd3) && add) {
// unwind what's been done, but don't care about success - what more could we do?
- snprintf(cmd, sizeof(cmd),
- "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
- (!add ? "A" : "D"),
- intIface, extIface);
- runCmd(IPTABLES_PATH, cmd);
-
- snprintf(cmd, sizeof(cmd),
- "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
- (!add ? "A" : "D"),
- extIface, intIface);
- runCmd(IPTABLES_PATH, cmd);
- return -1;
+ rc = -1;
+ goto err_return;
}
return 0;
+
+err_return:
+ cmd2[1] = "-D";
+ runCmd(ARRAY_SIZE(cmd2), cmd2);
+err_invalid_drop:
+ cmd1[1] = "-D";
+ runCmd(ARRAY_SIZE(cmd1), cmd1);
+ return rc;
}
// nat disable intface extface
// 0 1 2 3 4 5
// nat enable intface extface addrcnt nated-ipaddr/prelength
int NatController::disableNat(const int argc, char **argv) {
- char cmd[255];
int i;
int addrCount = atoi(argv[4]);
const char *intIface = argv[2];
secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
}
- runCmd(IP_PATH, "route flush cache");
+ const char *cmd[] = {
+ IP_PATH,
+ "route",
+ "flush",
+ "cache"
+ };
+ runCmd(ARRAY_SIZE(cmd), cmd);
}
if (--natCount <= 0) {
SecondaryTableController *secondaryTableCtrl;
int setDefaults();
- int runCmd(const char *path, const char *cmd);
+ int runCmd(int argc, const char **argv);
bool checkInterface(const char *iface);
int setForwardRules(bool set, const char *intIface, const char *extIface);
};
int execIptables(IptablesTarget target, ...);
int execIptablesSilently(IptablesTarget target, ...);
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
+
#endif
#define LOG_TAG "SecondaryTablController"
#include <cutils/log.h>
#include <cutils/properties.h>
-
-extern "C" int system_nosh(const char *command);
+#include <logwrap/logwrap.h>
#include "ResponseCode.h"
#include "NetdConstants.h"
int SecondaryTableController::modifyRoute(SocketClient *cli, const char *action, char *iface,
char *dest, int prefix, char *gateway, int tableIndex) {
- char *cmd;
+ char dest_str[44]; // enough to store an IPv6 address + 3 character bitmask
+ char tableIndex_str[11];
+ int ret;
+
+ // IP tool doesn't like "::" - the equiv of 0.0.0.0 that it accepts for ipv4
+ snprintf(dest_str, sizeof(dest_str), "%s/%d", dest, prefix);
+ snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex + BASE_TABLE_NUMBER);
if (strcmp("::", gateway) == 0) {
- // IP tool doesn't like "::" - the equiv of 0.0.0.0 that it accepts for ipv4
- asprintf(&cmd, "%s route %s %s/%d dev %s table %d",
- IP_PATH, action, dest, prefix, iface, tableIndex+BASE_TABLE_NUMBER);
+ const char *cmd[] = {
+ IP_PATH,
+ "route",
+ action,
+ dest_str,
+ "dev",
+ iface,
+ "table",
+ tableIndex_str
+ };
+ ret = runCmd(ARRAY_SIZE(cmd), cmd);
} else {
- asprintf(&cmd, "%s route %s %s/%d via %s dev %s table %d",
- IP_PATH, action, dest, prefix, gateway, iface, tableIndex+BASE_TABLE_NUMBER);
+ const char *cmd[] = {
+ IP_PATH,
+ "route",
+ action,
+ dest_str,
+ "via",
+ gateway,
+ "dev",
+ iface,
+ "table",
+ tableIndex_str
+ };
+ ret = runCmd(ARRAY_SIZE(cmd), cmd);
}
- if (runAndFree(cli, cmd)) {
+ if (ret) {
ALOGE("ip route %s failed: %s route %s %s/%d via %s dev %s table %d", action,
IP_PATH, action, dest, prefix, gateway, iface, tableIndex+BASE_TABLE_NUMBER);
errno = ENODEV;
int SecondaryTableController::modifyFromRule(int tableIndex, const char *action,
const char *addr) {
- char *cmd;
+ char tableIndex_str[11];
if (verifyTableIndex(tableIndex)) {
return -1;
}
- asprintf(&cmd, "%s %s rule %s from %s table %d", IP_PATH, getVersion(addr),
- action, addr, tableIndex + BASE_TABLE_NUMBER);
- if (runAndFree(NULL, cmd)) {
+
+ snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex +
+ BASE_TABLE_NUMBER);
+ const char *cmd[] = {
+ IP_PATH,
+ getVersion(addr),
+ "rule",
+ action,
+ "from",
+ addr,
+ "table",
+ tableIndex_str
+ };
+ if (runCmd(ARRAY_SIZE(cmd), cmd)) {
return -1;
}
int SecondaryTableController::modifyLocalRoute(int tableIndex, const char *action,
const char *iface, const char *addr) {
- char *cmd;
+ char tableIndex_str[11];
if (verifyTableIndex(tableIndex)) {
return -1;
modifyRuleCount(tableIndex, action); // some del's will fail as the iface is already gone.
- asprintf(&cmd, "%s route %s %s dev %s table %d", IP_PATH, action, addr, iface,
- tableIndex+BASE_TABLE_NUMBER);
- return runAndFree(NULL, cmd);
+ snprintf(tableIndex_str, sizeof(tableIndex_str), "%d", tableIndex +
+ BASE_TABLE_NUMBER);
+ const char *cmd[] = {
+ IP_PATH,
+ "route",
+ action,
+ addr,
+ "dev",
+ iface,
+ "table",
+ tableIndex_str
+ };
+
+ return runCmd(ARRAY_SIZE(cmd), cmd);
}
-int SecondaryTableController::runAndFree(SocketClient *cli, char *cmd) {
+int SecondaryTableController::runCmd(int argc, const char **argv) {
int ret = 0;
- if (strlen(cmd) >= 255) {
- if (cli != NULL) {
- ALOGE("ip command (%s) too long", cmd);
- errno = E2BIG;
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Too long", true);
- }
- free(cmd);
- return -1;
- }
- ret = system_nosh(cmd);
- free(cmd);
+
+ ret = android_fork_execvp(argc, (char **)argv, NULL, false, false);
return ret;
}
int verifyTableIndex(int tableIndex);
const char *getVersion(const char *addr);
- int runAndFree(SocketClient *cli, char *cmd);
+ int runCmd(int argc, const char **argv);
};
#endif
+++ /dev/null
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string.h>
-#include <sys/types.h>
-#include <sys/wait.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <errno.h>
-#include <fcntl.h>
-
-#include "private/android_filesystem_config.h"
-#include "cutils/log.h"
-
-#include <logwrap/logwrap.h>
-
-/*
- * The following is based off of bionic/libc/unistd/system.c with
- * modifications to avoid calling /system/bin/sh -c
- */
-int system_nosh(const char *command)
-{
- char buffer[255];
- char *argp[32];
- char *next = buffer;
- char *tmp;
- int i = 0;
- int rc;
- int status;
-
- if (!command) /* just checking... */
- return(1);
-
- /*
- * The command to argp splitting is from code that was
- * reverted in Change: 11b4e9b2
- */
- if (strnlen(command, sizeof(buffer) - 1) == sizeof(buffer) - 1) {
- ALOGE("command line too long while processing: %s", command);
- errno = E2BIG;
- return -1;
- }
- strcpy(buffer, command); // Command len is already checked.
- while ((tmp = strsep(&next, " "))) {
- argp[i++] = tmp;
- if (i == 32) {
- ALOGE("argument overflow while processing: %s", command);
- errno = E2BIG;
- return -1;
- }
- }
- argp[i] = NULL;
-
- rc = android_fork_execvp(i, argp, &status, false, false);
- if (rc)
- return rc;
- if (!WIFEXITED(status))
- return -ECHILD;
- return WEXITSTATUS(status);
-}
#define LOG_TAG "OemIptablesHook"
#include <cutils/log.h>
+#include <logwrap/logwrap.h>
#include "NetdConstants.h"
-extern "C" int system_nosh(const char *command);
-
-
-static int runIptablesCmd(const char *cmd) {
- char *buffer;
- size_t len = strnlen(cmd, 255);
+static int runIptablesCmd(int argc, const char **argv) {
int res;
- if (len == 255) {
- ALOGE("command too long");
- return -1;
- }
-
- asprintf(&buffer, "%s %s", IPTABLES_PATH, cmd);
- res = system_nosh(buffer);
- free(buffer);
+ res = android_fork_execvp(argc, (char **)argv, NULL, false, false);
return res;
}
static bool oemCleanupHooks() {
- runIptablesCmd("-F oem_out");
- runIptablesCmd("-F oem_fwd");
- runIptablesCmd("-t nat -F oem_nat_pre");
+ const char *cmd1[] = {
+ IPTABLES_PATH,
+ "-F",
+ "oem_out"
+ };
+ runIptablesCmd(ARRAY_SIZE(cmd1), cmd1);
+
+ const char *cmd2[] = {
+ IPTABLES_PATH,
+ "-F",
+ "oem_fwd"
+ };
+ runIptablesCmd(ARRAY_SIZE(cmd2), cmd2);
+
+ const char *cmd3[] = {
+ IPTABLES_PATH,
+ "-t",
+ "nat",
+ "-F",
+ "oem_nat_pre"
+ };
+ runIptablesCmd(ARRAY_SIZE(cmd3), cmd3);
return true;
}