summaryrefslogtreecommitdiffstats
path: root/meta-netboot/README.porter
blob: 7ab0803fb6aa2d96d821538bf1bbd925d2101f04 (plain)
1
2
3
4

@media only all and (prefers-color-scheme: dark) {
.highlight .hll { background-color: #49483e }
.highlight .c { color: #75715e } /* Comment */
.highlight .err { color: #960050; background-color: #1e0010 } /* Error */
.highlight .k { color: #66d9ef } /* Keyword */
.highlight .l { color: #ae81ff } /* Literal */
.highlight .n { color: #f8f8f2 } /* Name */
.highlight .o { color: #f92672 } /* Operator */
.highlight .p { color: #f8f8f2 } /* Punctuation */
.highlight .ch { color: #75715e } /* Comment.Hashbang */
.highlight .cm { color: #75715e } /* Comment.Multiline */
.highlight .cp { color: #75715e } /* Comment.Preproc */
.highlight .cpf { color: #75715e } /* Comment.PreprocFile */
.highlight .c1 { color: #75715e } /* Comment.Single */
.highlight .cs { color: #75715e } /* Comment.Special */
.highlight .gd { color: #f92672 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .gi { color: #a6e22e } /* Generic.Inserted */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #75715e } /* Generic.Subheading */
.highlight .kc { color: #66d9ef } /* Keyword.Constant */
.highligh
Below are the environment variables that can be set in the u-boot console to boot the porter board in various configurations

################## Common options #####################
# these options are common to all configurations:

setenv 'bootkfile' 'uImage+dtb'
setenv 'bootkaddr' '0x40007fc0'

setenv 'bootifile' 'initramfs-netboot-image-porter.ext4.gz.u-boot'
setenv 'bootiaddr' '0x50000000'

setenv 'bootargs_console' 'console=ttySC6,38400 ignore_loglevel'
setenv 'bootargs_video' 'vmalloc=384M video=HDMI-A-1:1280x960-32@60'
setenv 'bootargs_extra' 'rw rootfstype=ext4 rootwait rootdelay=2'

################ Boot on MMC (SDcard) #################

setenv 'bootargs_root' 'root=/dev/mmcblk0p1'
setenv 'bootmmc' '1:1'
setenv 'bootkload_sd' 'ext4load mmc ${bootmmc} ${bootkaddr} boot/${bootkfile}'
setenv 'bootcmd' 'setenv bootargs ${bootargs_console} ${bootargs_video} ${bootargs_root} ${bootargs_extra}; run bootkload_sd; bootm ${bootkaddr}'

################ Boot on MMC (SDcard) with initrd ######

setenv 'bootargs_root' 'root=/dev/ram0 ramdisk_size=16384'
setenv 'bootmmc' '1:1'
setenv 'bootkload_sd' 'ext4load mmc ${bootmmc} ${bootkaddr} boot/${bootkfile}'
setenv 'bootiload_sd' 'ext4load mmc ${bootmmc} ${bootiaddr} boot/${bootifile}'
setenv 'bootcmd' 'setenv bootargs ${bootargs_console} ${bootargs_video} ${bootargs_root} ${bootargs_extra}; run bootkload_sd; run bootiload_sd; bootm ${bootkaddr} ${bootiaddr}'

################ Netboot through TFTP+NBD ##################
# replace <IP> addresses by appropriate addresses

setenv 'ipaddr' '<board_IP>'
setenv 'serverip' '<server_IP>'

setenv 'bootargs_root' 'root=/dev/ram0 ramdisk_size=16384 ip=dhcp'
setenv 'bootkload_net' 'tftp ${bootkaddr} porter/${bootkfile}'
setenv 'bootiload_net' 'tftp ${bootiaddr} porter/${bootifile}'
setenv 'bootcmd' 'setenv bootargs ${bootargs_console} ${bootargs_video} ${bootargs_root} ${bootargs_extra} nbd.server=${serverip}; run bootkload_net; run bootiload_net; bootm ${bootkaddr} ${bootiaddr}'
} /* Literal.String.Char */ .highlight .dl { color: #e6db74 } /* Literal.String.Delimiter */ .highlight .sd { color: #e6db74 } /* Literal.String.Doc */ .highlight .s2 { color: #e6db74 } /* Literal.String.Double */ .highlight .se { color: #ae81ff } /* Literal.String.Escape */ .highlight .sh { color: #e6db74 } /* Literal.String.Heredoc */ .highlight .si { color: #e6db74 } /* Literal.String.Interpol */ .highlight .sx { color: #e6db74 } /* Literal.String.Other */ .highlight .sr { color: #e6db74 } /* Literal.String.Regex */ .highlight .s1 { color: #e6db74 } /* Literal.String.Single */ .highlight .ss { color: #e6db74 } /* Literal.String.Symbol */ .highlight .bp { color: #f8f8f2 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #a6e22e } /* Name.Function.Magic */ .highlight .vc { color: #f8f8f2 } /* Name.Variable.Class */ .highlight .vg { color: #f8f8f2 } /* Name.Variable.Global */ .highlight .vi { color: #f8f8f2 } /* Name.Variable.Instance */ .highlight .vm { color: #f8f8f2 } /* Name.Variable.Magic */ .highlight .il { color: #ae81ff } /* Literal.Number.Integer.Long */ } @media (prefers-color-scheme: light) { .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
/*
 * Copyright (C) 2016 "IoT.bzh"
 * Author Fulup Ar Foll <fulup@iot.bzh>
 *
 * 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, something express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Reference:
 *   Json load using json_unpack https://jansson.readthedocs.io/en/2.9/apiref.html#parsing-and-validating-values
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/time.h>

#include "filescan-utils.h"
#include "ctl-config.h"

extern void* getExternalData(CtlConfigT *ctlConfig) {
    return ctlConfig->external;
}

extern void setExternalData(CtlConfigT *ctlConfig, void *data) {
    ctlConfig->external = data;
}
int CtlConfigMagicNew() {
  static int InitRandomDone=0;

  if (!InitRandomDone) {
    struct timeval tv;
    InitRandomDone=1;
    gettimeofday(&tv,NULL);
    srand ((int)tv.tv_usec);
  }

  return ((long)rand());
}

json_object* CtlConfigScan(const char *dirList, const char *prefix) {
    char controlFile[CONTROL_MAXPATH_LEN];
    const char *binderName = GetBinderName();

    controlFile[CONTROL_MAXPATH_LEN - 1] = '\0';

    if(prefix && prefix[0] != '\0') {
        strncpy(controlFile, prefix, CONTROL_MAXPATH_LEN - 1);
        strncat(controlFile, "-", CONTROL_MAXPATH_LEN - strlen(controlFile) - 1);
        strncat(controlFile, binderName, CONTROL_MAXPATH_LEN - strlen(controlFile) - 1);
    }
    else {
        strncpy(controlFile, binderName, CONTROL_MAXPATH_LEN - 1);
    }
    // search for default dispatch config file
    json_object* responseJ = ScanForConfig(dirList, CTL_SCAN_RECURSIVE, controlFile, ".json");

    return responseJ;
}

char* ConfigSearch(afb_api_t apiHandle, json_object *responseJ) {
    // We load 1st file others are just warnings
    size_t p_length;
    char *filepath = NULL;
    const char *filename;
    const char*fullpath;

    for (int index = 0; index < json_object_array_length(responseJ); index++) {
        json_object *entryJ = json_object_array_get_idx(responseJ, index);

        int err = wrap_json_unpack(entryJ, "{s:s, s:s !}", "fullpath", &fullpath, "filename", &filename);
        if (err) {
            AFB_API_ERROR(apiHandle, "CTL-INIT HOOPs invalid JSON entry= %s", json_object_get_string(entryJ));
        }

        if (index == 0) {
            p_length = strlen(fullpath) + 1 + strlen(filename);
            filepath = malloc(p_length + 1);

            strncpy(filepath, fullpath, p_length);
            strncat(filepath, "/", p_length - strlen(filepath));
            strncat(filepath, filename, p_length - strlen(filepath));
        }
        else {
            AFB_API_WARNING(apiHandle, "CTL-INIT JSON file found but not used : %s/%s", fullpath, filename);
        }
    }

    json_object_put(responseJ);
    return filepath;
}

char* CtlConfigSearch(afb_api_t apiHandle, const char *dirList, const char *prefix) {
    // search for default dispatch config file
    json_object* responseJ = CtlConfigScan (dirList, prefix);

    if(responseJ)
        return ConfigSearch(apiHandle, responseJ);

    return NULL;
}

static int DispatchRequireOneApi(afb_api_t apiHandle, json_object * bindindJ) {
    const char* requireBinding = json_object_get_string(bindindJ);
    int err = afb_api_require_api(apiHandle, requireBinding, 1);
    if (err) {
        AFB_API_WARNING(apiHandle, "CTL-LOAD-CONFIG:REQUIRE Fail to get=%s", requireBinding);
    }

    return err;
}

/**
 * @brief Best effort to initialise everything before starting
 * Call afb_require_api at the first call or if there was an error because
 * the CtlConfigExec could be called anywhere and not in binding init.
 * So you could call this function at init time.
 *
 * @param apiHandle : a afb_daemon api handle, see afb_api_t in afb_definitions.h
 * @param requireJ : json_object array of api name required.
 */
void DispatchRequireApi(afb_api_t apiHandle, json_object * requireJ) {
    static int init = 0, err = 0;
    int idx;

    if ( (! init || err) && requireJ) {
        if (json_object_get_type(requireJ) == json_type_array) {
            for (idx = 0; idx < json_object_array_length(requireJ); idx++) {
                err += DispatchRequireOneApi(apiHandle, json_object_array_get_idx(requireJ, idx));
            }
        } else {
            err += DispatchRequireOneApi(apiHandle, requireJ);
        }
    }

    init = 1;
}

int CtlConfigExec(afb_api_t apiHandle, CtlConfigT *ctlConfig) {

    DispatchRequireApi(apiHandle, ctlConfig->requireJ);
#ifdef CONTROL_SUPPORT_LUA
    // load static LUA utilities
    LuaConfigExec(apiHandle);
#endif

    // Loop on every section and process config
    int errcount=0;
    for (int idx = 0; ctlConfig->sections[idx].key != NULL; idx++) {
        if (!ctlConfig->sections[idx].loadCB)
            AFB_API_NOTICE(apiHandle, "CtlConfigLoad: notice empty section '%s'", ctlConfig->sections[idx].key);
        else
            errcount += ctlConfig->sections[idx].loadCB(apiHandle, &ctlConfig->sections[idx], NULL);
    }

    return errcount;
}

CtlConfigT *CtlLoadMetaDataJson(afb_api_t apiHandle, json_object *ctlConfigJ, const char *prefix) {
    json_object *metadataJ;
    CtlConfigT *ctlHandle=NULL;

    int done = json_object_object_get_ex(ctlConfigJ, "metadata", &metadataJ);
    if (done) {
        ctlHandle = calloc(1, sizeof (CtlConfigT));
        if (wrap_json_unpack(metadataJ, "{ss,ss,ss,s?s,s?o,s?s,s?s !}",
                                        "uid", &ctlHandle->uid,
                                        "version", &ctlHandle->version,
                                        "api", &ctlHandle->api,
                                        "info", &ctlHandle->info,
                                        "require", &ctlHandle->requireJ,
                                        "author", &ctlHandle->author,
                                        "date", &ctlHandle->date)) {
            AFB_API_ERROR(apiHandle, "CTL-LOAD-CONFIG:METADATA Missing something uid|api|version|[info]|[require]|[author]|[date] in:\n-- %s", json_object_get_string(metadataJ));
            free(ctlHandle);
            return NULL;
        }
        ctlHandle->configJ = ctlConfigJ;
        ctlHandle->prefix = prefix;
    }

    return ctlHandle;
}

CtlConfigT *CtlLoadMetaDataUsingPrefix(afb_api_t apiHandle,const char* filepath, const char *prefix) {
    json_object *ctlConfigJ;


    // Load JSON file
    ctlConfigJ = json_object_from_file(filepath);
    if (!ctlConfigJ) {
        AFB_API_ERROR(apiHandle, "CTL-LOAD-CONFIG Not invalid JSON %s ", filepath);
        return NULL;
    }

    AFB_API_INFO(apiHandle, "CTL-LOAD-CONFIG: loading config filepath=%s", filepath);

    return CtlLoadMetaDataJson(apiHandle, ctlConfigJ, prefix);
}

void wrap_json_array_add(void* array, json_object *val) {
    json_object_array_add(array, (json_object*)val);
}

json_object* LoadAdditionalsFiles(afb_api_t apiHandle, CtlConfigT *ctlHandle, const char *key, json_object *sectionJ);

json_object* CtlUpdateSectionConfig(afb_api_t apiHandle, CtlConfigT *ctlHandle, const char *key, json_object *sectionJ, json_object *filesJ) {

    json_object *sectionArrayJ;
    char *oneFile = NULL;
    const char *bindingPath = GetBindingDirPath(apiHandle);

    if(! json_object_is_type(sectionJ, json_type_array)) {
        sectionArrayJ = json_object_new_array();
        if(json_object_object_length(sectionJ) > 0)
            json_object_array_add(sectionArrayJ, sectionJ);
    }
    else sectionArrayJ = sectionJ;

    json_object_get(sectionJ);
    json_object_object_del(ctlHandle->configJ, key);
    json_object_object_add(ctlHandle->configJ, key, sectionArrayJ);

    if (json_object_get_type(filesJ) == json_type_array) {
        int length = (int)json_object_array_length(filesJ);
        for (int idx=0; idx < length; idx++) {
            json_object *oneFileJ = json_object_array_get_idx(filesJ, idx);
            json_object *responseJ =
                ScanForConfig(bindingPath, CTL_SCAN_RECURSIVE, json_object_get_string(oneFileJ), ".json");
            if(!responseJ) {
                AFB_API_ERROR(apiHandle, "No config files found in search path. No changes has been made\n -- %s", bindingPath);
                return sectionArrayJ;
            }
            oneFile = ConfigSearch(apiHandle, responseJ);
            if (oneFile) {
                json_object *newSectionJ, *newFileJ = json_object_from_file(oneFile);
                json_object_object_get_ex(newFileJ, key, &newSectionJ);
                json_object_get(newSectionJ);
                json_object_put(newFileJ);
                LoadAdditionalsFiles(apiHandle, ctlHandle, key, newSectionJ);
                json_object_object_get_ex(ctlHandle->configJ, key, &sectionArrayJ);
                wrap_json_optarray_for_all(newSectionJ, wrap_json_array_add, sectionArrayJ);
            }
        }
    } else {
        json_object *responseJ =
            ScanForConfig(bindingPath, CTL_SCAN_RECURSIVE, json_object_get_string(filesJ), ".json");
        if(!responseJ) {
            AFB_API_ERROR(apiHandle, "No config files found in search path. No changes has been made\n -- %s", bindingPath);
            return sectionArrayJ;
        }
        oneFile = ConfigSearch(apiHandle, responseJ);
        json_object *newSectionJ = json_object_from_file(oneFile);
        LoadAdditionalsFiles(apiHandle, ctlHandle, key, newSectionJ);
        wrap_json_optarray_for_all(newSectionJ, wrap_json_array_add, sectionArrayJ);
    }

    free(oneFile);
    return sectionArrayJ;
}

json_object* LoadAdditionalsFiles(afb_api_t apiHandle, CtlConfigT *ctlHandle, const char *key, json_object *sectionJ)
{
    json_object *filesJ, *filesArrayJ = json_object_new_array();
    if (json_object_get_type(sectionJ) == json_type_array) {
        int length = (int)json_object_array_length(sectionJ);
        for (int idx=0; idx < length; idx++) {
            json_object *obj = json_object_array_get_idx(sectionJ, idx);
            int hasFiles = json_object_object_get_ex(obj, "files", &filesJ);
            if(hasFiles) {
                // Clean files key as we don't want to make infinite loop
                json_object_get(filesJ);
                json_object_object_del(obj, "files");
                if(json_object_is_type(filesJ, json_type_array))
                    wrap_json_array_for_all(filesJ, wrap_json_array_add, filesArrayJ);
                else
                    json_object_array_add(filesArrayJ, filesJ);
            }
        }
    } else {
        int hasFiles = json_object_object_get_ex(sectionJ, "files", &filesJ);
        if(hasFiles) {
            // Clean files key as we don't want to make infinite loop
            json_object_get(filesJ);
            json_object_object_del(sectionJ, "files");
            if(json_object_is_type(filesJ, json_type_array))
                filesArrayJ = filesJ;
            else
                json_object_array_add(filesArrayJ, filesJ);
        }
    }

    if(json_object_array_length(filesArrayJ) > 0)
        sectionJ = CtlUpdateSectionConfig(apiHandle, ctlHandle, key, sectionJ, filesArrayJ);
    json_object_put(filesArrayJ);
    return sectionJ;
}

int CtlLoadSections(afb_api_t apiHandle, CtlConfigT *ctlHandle, CtlSectionT *sections) {
    int err;

#ifdef CONTROL_SUPPORT_LUA
    err= LuaConfigLoad(apiHandle, ctlHandle->prefix);
    if (err)
        return 1;
#endif

    err = 0;
    ctlHandle->sections = sections;
    for (int idx = 0; sections[idx].key != NULL; idx++) {
        json_object * sectionJ;
        int done = json_object_object_get_ex(ctlHandle->configJ, sections[idx].key, &sectionJ);
        if (done) {
            sections[idx].prefix = ctlHandle->prefix;
            json_object* updatedSectionJ = LoadAdditionalsFiles(apiHandle, ctlHandle, sections[idx].key, sectionJ);
            err += sections[idx].loadCB(apiHandle, &sections[idx], updatedSectionJ);
        }
    }
    if (err)
        return 1;

    return 0;
}