SDK Script dynamic-operator.are

dynamic-operator.are
/* DESC: This script will scan Mobile2 and dial the appropriate SIM on Mobile1
 * Copyright (C) 2013 NetModule AG, Switzerland
 *
 */
 
template operator {
    prio;
    sim;
    lai;
    apn;
    username;
    password;
 
    int operator (int p, int s, string l, string a, string usr, string pwd) {
        this.prio = p;
        this.sim = s;
        this.lai = l;
        this.apn = a;
        this.username = usr;
        this.password = pwd;
        return 0;
    }
};
 
ME = argv[0];           /* script path */
ARGV1 = argv[1];        /* 1st script argument */
FORCE = 0;              /* force operator switch even if wanlink is up */
VALID_ONLY = 1;         /* only register to networks which are allowed by SIM */
SCAN_INTERVAL = 30;     /* network scan interval in seconds */
TEST = 0;               /* run test */
 
/* example list of operators */
OPCNT = 0;
OPERATORS = mkarray();
 
OPERATORS[OPCNT++] = new operator
                        (1,                             /* prio */
                         0,                             /* SIM1 */
                         "22801",                       /* lai */
                         "corporate.swisscom.ch",       /* apn */
                         "testprofil",                  /* username */
                         "temporary"                    /* password */
                        );
OPERATORS[OPCNT++] = new operator
                        (2,                             /* prio */
                         1,                             /* SIM2 */
                         "22802",                       /* lai */
                         "internet",                    /* apn */
                         "internet",                    /* username */
                         "internet"                     /* password */
                        );
OPERATORS[OPCNT++] = new operator
                        (3,                             /* prio */
                         2,                             /* EXT1 */
                         "26201",                       /* lai */
                         "internet.telekom",            /* apn */
                         "tm",                          /* username */
                         ""                             /* password */
                        );
 
 
/* creates file at path */
int touch (string path)
{
    fd = open(path, O_CREAT | O_WRONLY);
    if (fd < 0) return -1;
    close(fd);
    return 0;
}
 
/* checks if path exists */
int exists (string path)
{
    fd = open(path, O_RDONLY);
    if (fd < 0) return 0;
    close(fd);
    return 1;
}
 
/* copies a file */
int copy (string from, string to)
{
    fpin = fopen(from, "r");
    if (is_void(fpin)) {
        nb_syslog("unable to open %s", from);
        return -1;
    }
    fpout = fopen(to, "w+");
    if (is_void(fpout)) {
        fclose(fpin);
        nb_syslog("unable to open %s", to);
        return -1;
    }
    while (1) {
        buf = fread(fpin, 1024);
        if (is_void(buf)) break;
 
        len = strlen(buf);
        if (len == 0) break;
 
        fwrite(fpout, buf, len);
    }
    fclose(fpin);
    fclose(fpout);
 
    return 0;
}
 
/* mkdir -p */
int makedir (string path, int mode)
{
    pc = explode(path);
 
    if (pc[0] != "/") {
        nb_syslog("no absolute directory given");
        return -1;
    }
 
    len = length(pc);
    if (pc[len-1] != "/") pc[len++] = "/";
    p = "";
 
    for (i = 0; i < len; i++) {
        if (pc[i] == "/" && i > 0) {
            mkdir(p, mode);
        }
        p = strcat(p, pc[i]);
    }
 
    return 0;
}
 
/* returns operator index for specified lai */
int find_operator (string lai)
{
    if (lai != "") {
        /* search by lai first */
        for (o = 0; o < OPCNT; o++) {
            if (OPERATORS[o].lai == lai) return o;
        }
    }
 
    return -1; /* not a known operator */
}
 
/* returns wwan interface index for specified sim index */
int find_wwan_interface (int sim)
{
    for (o = 0; o < OPCNT; o++) {
        op = OPERATORS[o];
 
        if (op.sim == sim) {
            return o;
        }
    }
    return -1; /* no wwan interface with this sim */
}
 
/* returns current wwan interface index */
int current_operator ()
{
    n = nb_config_get("wanlink.0.name");
    if (strlen(n) >= 5 && left(n, 4) == "wwan") {
        return (int)substr(n, 4, 1);
    }
 
    return -1; /* not configured */
}
 
/* activates specified operator */
int activate_operator (int op)
{
    current = current_operator();
 
    if (current != -1 && current == op) {
        nb_syslog("wwan%d is already active", op);
        return 0;
    }
    nb_syslog("switching to wwan%d", op);
 
    if (nb_config_set(sprintf("wanlink.0.name=wwan%d", op)) != 0) {
        nb_syslog("unable to activate wwan%d", op);
        return -1;
    }
    /* re-configuration triggered */
    nb_syslog("activated wwan%d", op);
 
    /* give modem some time to register */
    nb_syslog("waiting for wwan%d to come up", op);
    sleep(30); /* needed for SIM switch */
 
    /* wait for wanlink to come up */
    for (i = 0; i < 20; i++) {
        sleep(10);
 
        nb_syslog("checking if wwan%d has come up", op);
        st = nb_status("wan");
 
        ifc = struct_get(st, "WANLINK1_INTERFACE");
        if (ifc != sprintf("WWAN%d", op+1)) {
            nb_syslog("wwan%d is not yet active");
            continue;
        }
        state = struct_get(st, "WANLINK1_STATE");
        if (state == "up") {
            nb_syslog("wwan%d is up now", op);
            return 0;
        }
        attempts = (int) struct_get(st, "WANLINK1_DIAL_ATTEMPTS");
        nb_syslog("wwan%d has dialed for %d times", op, attempts);
        if (attempts > 2) {
            nb_syslog("giving up after %d dial attempts", attempts);
            return -1;
        }
    }
    nb_syslog("giving up waiting for wanlink");
 
    return 0;
}
 
/* returns true if wanlink is up */
bool wanup ()
{
    st = nb_status("wan");
    state = struct_get(st, "WANLINK1_STATE");
    return (state == "up");
}
 
/* scan test */
struct nb_scan_test ()
{
    return mkstruct(
        "NETWORK_COUNT", 2,
 
        "NETWORK1_NAME", "swisscom",
        "NETWORK1_LAI", "22801",
        "NETWORK1_STATUS", "available",
 
        "NETWORK2_NAME", "telekom",
        "NETWORK2_LAI", "26201",
        "NETWORK2_STATUS", "available"
    );
}
 
/* installs config and script */
int install ()
{
    path = "/config";
    cfg = sprintf("%s/user-config.cfg", path);
 
    makedir(sprintf("%s/files", path), S_IRUSR|S_IWUSR|S_IXUSR);
 
    touch(cfg);
    fp = fopen(cfg, "w");
    if (!fp) {
        printf("ERROR: unable to open %s\n", cfg);
        return -1;
    }
 
    /* basic system config */
    fwrite(fp, "config.version=1.3\n");
    fwrite(fp, sprintf("admin.password=%s\n", nb_config_get("admin.password")));
 
    /* set up OPERATORS */
    for (o = 0; o < OPCNT; o++) {
        op = OPERATORS[o];
        fwrite(fp, sprintf("sim.%d.lai=%s\n", op.sim, op.lai));
        fwrite(fp, sprintf("wwan.%d.status=1\n", o));
        fwrite(fp, sprintf("wwan.%d.card=0\n", o));  /* force wwan0 */
        fwrite(fp, sprintf("wwan.%d.sim=%d\n", o, op.sim));
        fwrite(fp, sprintf("wwan.%d.number=*99***1#\n", o));
        fwrite(fp, sprintf("wwan.%d.username=%s\n", o, op.usename));
        fwrite(fp, sprintf("wwan.%d.password=%s\n", o, op.password));
        fwrite(fp, sprintf("wwan.%d.apn=%s\n", o, op.apn));
    }
 
    /* enable first wwan interface */
    fwrite(fp, "wanlink.0.name=wwan0\n");
    fwrite(fp, "wanlink.0.prio=1\n");
    fwrite(fp, "wanlink.0.weight=0\n");
    fwrite(fp, "wanlink.0.mode=1\n");
 
    /* install this script */
    fwrite(fp, "sdk.status=1\n");
    fwrite(fp, "sdk.trigger.0.name=SDK-STARTUP\n");
    fwrite(fp, "sdk.trigger.0.event=sdk-startup\n");
    fwrite(fp, "sdk.script.0.name=dynamic-operator\n");
    fwrite(fp, "sdk.job.0.name=DYNAMIC-OPERATOR\n");
    fwrite(fp, "sdk.job.0.trigger=0\n");
    fwrite(fp, "sdk.job.0.script=0\n");
 
    scriptpath = sprintf("%s/files/home/sdk/scripts/uploaded", path);
    makedir(scriptpath, S_IRUSR|S_IWUSR|S_IXUSR);
    fclose(fp);
 
    if (copy(ME, sprintf("%s/dynamic-operator", scriptpath)) != 0) {
        printf("ERROR: cannot copy script");
        return -1;
    }
 
    return nb_update_config(sprintf("dir://%s", path));
}
 
 
/* -------------------------------- main -------------------------------- */
 
nb_syslog("starting %s", ME);
 
 
if (ARGV1 == "install") {
    /* script installation requested */
    printf("installing script and its config\n");
 
    if (install() != 0) {
        printf("unable to install script\n");
        exit(1);
    } else {
        printf("successfully installed script\n");
    }
    /* script will terminate at this point */
    exit(0);
} 
 
/* re-read operators */
OPCNT = 0;
OPERATORS = mkarray();
 
for (i = 0; i < 14; i++) {
    status = nb_config_get(sprintf("wwan.%d.status", i));
    if (status != "1") {
        continue;
    }
 
    sim = nb_config_get(sprintf("wwan.%d.sim", i));
    if (sim < 0) {
        nb_syslog("invalid sim for wwan%d", i);
        continue;
    }
 
    lai = nb_config_get(sprintf("sim.%d.lai", sim));
 
    OPERATORS[OPCNT].prio = i;
    OPERATORS[OPCNT].lai = lai;
    OPERATORS[OPCNT].sim = sim;
    OPERATORS[OPCNT].username = nb_config_get(sprintf("wwan.%d.username", i));
    OPERATORS[OPCNT].password = nb_config_get(sprintf("wwan.%d.password", i));
    OPERATORS[OPCNT].apn = nb_config_get(sprintf("wwan.%d.apn", i));
 
    nb_syslog("added operator%d (prio %d, sim %d, lai %s)", 
                OPCNT, i, sim, lai);
 
    OPCNT++;
}
 
while (1) {
    nb_syslog("sleeping");
    sleep(SCAN_INTERVAL);
 
    /* get wanlink state */
    up = wanup();
    if (up) {
        nb_syslog("wanlink is up");
    } else {
        nb_syslog("wanlink is down");
    }
 
    /* get current operator */
    current = current_operator();
    if (current != -1) {
        nb_syslog("current is wwan%d", current);
    }
 
    if (up && current != -1) {
        nb_syslog("wwan%d is up", current);
        if (FORCE != 1) continue;
    }
 
    /* scan networks */
    nb_syslog("scanning networks");
    if (TEST == 1) {
        nets = nb_scan_test();
    } else {
        nets = nb_scan_networks("Mobile2");
    }
    nr_nets = struct_get(nets, "NETWORK_COUNT");
 
    if (is_void(nr_nets) || nr_nets < 1) {
        nb_syslog("no networks found");
        continue;
    }
 
    nb_syslog("%d networks found", nr_nets);
 
    best = -1;
    prio = -1;
    for (i = 1; i <= nr_nets; i++) {
        net = trim(struct_get(nets, sprintf("NETWORK%d_NAME", i)));
        lai = trim(struct_get(nets, sprintf("NETWORK%d_LAI", i)));
        status = trim(struct_get(nets, sprintf("NETWORK%d_STATUS", i)));
 
        nb_syslog("detected network '%s' (lai %s, status %s)", net, lai, status);
 
        if (VALID_ONLY == 1 && status != "available") continue;
 
        op = find_operator(lai, net);
        if (op == -1) {
            nb_syslog("network '%s' does not belong to a known operator", net);
            continue;
        }
        if (current != -1 && op == current) {
            /* skip current operator */
            continue;
        }
        if (prio == -1 || OPERATORS[op].prio < prio) {
            best = op;
            prio = OPERATORS[op].prio;
        }
    }
    if (best != -1) {
        /* activate best operator */
        activate_operator(best);
    } else {
        nb_syslog("no better operator found");
    }
}
 
exit(0);