480 lines
15 KiB
C
480 lines
15 KiB
C
/*
|
|
* Copyright (c) 2017 Digi International Inc.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
* AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
* =======================================================================
|
|
*/
|
|
|
|
#include <aws_iot_config.h>
|
|
#include <aws_iot_log.h>
|
|
#include <confuse.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "aws_config.h"
|
|
|
|
/*------------------------------------------------------------------------------
|
|
D E F I N I T I O N S
|
|
------------------------------------------------------------------------------*/
|
|
#define SETTING_THING_NAME "thing_name"
|
|
#define SETTING_CLIENT_ID "client_id"
|
|
#define SETTING_HOST "host"
|
|
#define SETTING_PORT "port"
|
|
#define SETTING_CERTS_PATH "certs_path"
|
|
#define SETTING_ROOTCA_NAME "rootca_filename"
|
|
#define SETTING_SCERT_NAME "signed_cert_filename"
|
|
#define SETTING_PRIVKEY_NAME "private_key_filename"
|
|
#define SETTING_SHADOW_REPORT_RATE "shadow_report_rate"
|
|
#define SETTING_SHADOW_REPORT_RATE_MIN 1 /* second */
|
|
#define SETTING_SHADOW_REPORT_RATE_MAX (365 * 24 * 60 * 60UL) /* A year */
|
|
#define SETTING_TEMP_VARIATION "temperature_variation"
|
|
#define SETTING_TEMP_VARIATION_MIN 0.1 /* C */
|
|
#define SETTING_TEMP_VARIATION_MAX 10.0 /* C */
|
|
#define SETTING_CPULOAD_VARIATION "cpu_load_variation"
|
|
#define SETTING_CPULOAD_VARIATION_MIN 1 /* % */
|
|
#define SETTING_CPULOAD_VARIATION_MAX 50.0 /* % */
|
|
#define SETTING_USER_LED "user_led"
|
|
|
|
#define SETTING_UNKNOWN "__unknown"
|
|
|
|
/*------------------------------------------------------------------------------
|
|
F U N C T I O N D E C L A R A T I O N S
|
|
------------------------------------------------------------------------------*/
|
|
static int fill_aws_iot_config(aws_iot_cfg_t *aws_cfg);
|
|
static int cfg_check_thing_name(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_client_id(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_port(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_certificates_path(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_cert_file(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_shadow_report_rate(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_temp_variation(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_cpuload_variation(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_int_range(cfg_t *cfg, cfg_opt_t *opt, uint32_t min, uint32_t max);
|
|
static int cfg_check_float_range(cfg_t *cfg, cfg_opt_t *opt, float min, float max);
|
|
static int cfg_check_empty_string(cfg_t *cfg, cfg_opt_t *opt);
|
|
static int cfg_check_string_length(cfg_t *cfg, cfg_opt_t *opt, uint16_t min, uint16_t max);
|
|
static int file_exists(const char *const filename);
|
|
static int file_readable(const char *const filename);
|
|
|
|
/*------------------------------------------------------------------------------
|
|
G L O B A L V A R I A B L E S
|
|
------------------------------------------------------------------------------*/
|
|
static cfg_t *cfg;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
F U N C T I O N D E F I N I T I O N S
|
|
------------------------------------------------------------------------------*/
|
|
/*
|
|
* parse_configuration() - Parse and save the settings of a configuration file
|
|
*
|
|
* @filename: Name of the file containing the configuration settings.
|
|
* @aws_cfg: AWS IoT Device SDK configuration struct (aws_iot_cfg_t) where
|
|
* the settings parsed from the configuration file are saved.
|
|
*
|
|
* Read the provided configuration file and save the settings in the given
|
|
* aws_iot_cfg_t struct. If the file does not exist or cannot be read, the
|
|
* configuration struct is initialized with the default settings.
|
|
*
|
|
* Return: 0 if the file is parsed successfully, -1 if there is an error
|
|
* parsing the file.
|
|
*/
|
|
int parse_configuration(const char *const filename, aws_iot_cfg_t *aws_cfg)
|
|
{
|
|
/* Overall structure of the settings. */
|
|
static cfg_opt_t opts[] = {
|
|
/* --------------------------------------------------------------------- */
|
|
/*| TYPE | SETTING NAME | DEFAULT VALUE | FLAGS |*/
|
|
/* --------------------------------------------------------------------- */
|
|
CFG_STR (SETTING_THING_NAME, AWS_IOT_MY_THING_NAME, CFGF_NONE),
|
|
CFG_STR (SETTING_CLIENT_ID, AWS_IOT_MQTT_CLIENT_ID, CFGF_NONE),
|
|
CFG_STR (SETTING_HOST, AWS_IOT_MQTT_HOST, CFGF_NONE),
|
|
CFG_INT (SETTING_PORT, AWS_IOT_MQTT_PORT, CFGF_NONE),
|
|
|
|
CFG_STR (SETTING_CERTS_PATH, DEFAULT_CERTS_PATH, CFGF_NONE),
|
|
CFG_STR (SETTING_ROOTCA_NAME, AWS_IOT_ROOT_CA_FILENAME,CFGF_NONE),
|
|
CFG_STR (SETTING_SCERT_NAME, AWS_IOT_CERTIFICATE_FILENAME,CFGF_NONE),
|
|
CFG_STR (SETTING_PRIVKEY_NAME, AWS_IOT_PRIVATE_KEY_FILENAME,CFGF_NONE),
|
|
|
|
CFG_INT (SETTING_SHADOW_REPORT_RATE, 60, CFGF_NONE),
|
|
CFG_FLOAT (SETTING_TEMP_VARIATION, 1, CFGF_NONE),
|
|
CFG_FLOAT (SETTING_CPULOAD_VARIATION, 10, CFGF_NONE),
|
|
|
|
CFG_INT (SETTING_USER_LED, -1, CFGF_NONE),
|
|
|
|
/* Needed for unknown settings. */
|
|
CFG_STR (SETTING_UNKNOWN, NULL, CFGF_NONE),
|
|
CFG_END()
|
|
};
|
|
|
|
if (!file_exists(filename)) {
|
|
IOT_ERROR("File '%s' does not exist.", filename);
|
|
return -1;
|
|
}
|
|
|
|
if (!file_readable(filename)) {
|
|
IOT_ERROR("File '%s' cannot be read.", filename);
|
|
return -1;
|
|
}
|
|
|
|
cfg = cfg_init(opts, CFGF_IGNORE_UNKNOWN);
|
|
cfg_set_validate_func(cfg, SETTING_THING_NAME, cfg_check_thing_name);
|
|
cfg_set_validate_func(cfg, SETTING_CLIENT_ID, cfg_check_client_id);
|
|
cfg_set_validate_func(cfg, SETTING_HOST, cfg_check_empty_string);
|
|
cfg_set_validate_func(cfg, SETTING_PORT, cfg_check_port);
|
|
cfg_set_validate_func(cfg, SETTING_CERTS_PATH, cfg_check_certificates_path);
|
|
cfg_set_validate_func(cfg, SETTING_ROOTCA_NAME, cfg_check_cert_file);
|
|
cfg_set_validate_func(cfg, SETTING_SCERT_NAME, cfg_check_cert_file);
|
|
cfg_set_validate_func(cfg, SETTING_PRIVKEY_NAME, cfg_check_cert_file);
|
|
cfg_set_validate_func(cfg, SETTING_SHADOW_REPORT_RATE, cfg_check_shadow_report_rate);
|
|
cfg_set_validate_func(cfg, SETTING_TEMP_VARIATION, cfg_check_temp_variation);
|
|
cfg_set_validate_func(cfg, SETTING_CPULOAD_VARIATION, cfg_check_cpuload_variation);
|
|
|
|
/* Parse the configuration file. */
|
|
switch (cfg_parse(cfg, filename)) {
|
|
case CFG_FILE_ERROR:
|
|
IOT_ERROR("Configuration file '%s' could not be read: %s\n",
|
|
filename, strerror(errno));
|
|
return -1;
|
|
case CFG_SUCCESS:
|
|
break;
|
|
case CFG_PARSE_ERROR:
|
|
IOT_ERROR("Error parsing configuration file '%s'\n", filename);
|
|
return -1;
|
|
}
|
|
|
|
return fill_aws_iot_config(aws_cfg);
|
|
}
|
|
|
|
|
|
/*
|
|
* free_configuration() - Release the configuration var
|
|
*/
|
|
void free_configuration(void)
|
|
{
|
|
cfg_free(cfg);
|
|
}
|
|
|
|
/*
|
|
* fill_aws_iot_config() - Fill the AWS IoT Device SDK configuration struct
|
|
*
|
|
* @aws_cfg: AWS IoT Device SDK configuration struct (aws_iot_cfg_t).
|
|
*
|
|
* Return: 0 if the configuration is filled successfully, -1 otherwise.
|
|
*/
|
|
static int fill_aws_iot_config(aws_iot_cfg_t *aws_cfg)
|
|
{
|
|
aws_cfg->thing_name = cfg_getstr(cfg, SETTING_THING_NAME);
|
|
if (aws_cfg->thing_name == NULL)
|
|
return -1;
|
|
aws_cfg->client_id = cfg_getstr(cfg, SETTING_CLIENT_ID);
|
|
if (aws_cfg->client_id == NULL)
|
|
return -1;
|
|
aws_cfg->host = cfg_getstr(cfg, SETTING_HOST);
|
|
if (aws_cfg->host == NULL)
|
|
return -1;
|
|
aws_cfg->port = cfg_getint(cfg, SETTING_PORT);
|
|
aws_cfg->certs_path = cfg_getstr(cfg, SETTING_CERTS_PATH);
|
|
if (aws_cfg->certs_path == NULL)
|
|
return -1;
|
|
aws_cfg->rootca_fname = cfg_getstr(cfg, SETTING_ROOTCA_NAME);
|
|
if (aws_cfg->rootca_fname == NULL)
|
|
return -1;
|
|
aws_cfg->signed_cert_fname = cfg_getstr(cfg, SETTING_SCERT_NAME);
|
|
if (aws_cfg->signed_cert_fname == NULL)
|
|
return -1;
|
|
aws_cfg->priv_key_fname = cfg_getstr(cfg, SETTING_PRIVKEY_NAME);
|
|
if (aws_cfg->priv_key_fname == NULL)
|
|
return -1;
|
|
aws_cfg->shadow_report_rate = cfg_getint(cfg, SETTING_SHADOW_REPORT_RATE);
|
|
aws_cfg->temp_variation = cfg_getfloat(cfg, SETTING_TEMP_VARIATION);
|
|
aws_cfg->cpuload_variation = cfg_getfloat(cfg, SETTING_CPULOAD_VARIATION);
|
|
aws_cfg->led_gpio = cfg_getint(cfg, SETTING_USER_LED);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cfg_check_thing_name() - Validate thing_name in the configuration file
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_thing_name(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
return cfg_check_string_length(cfg, opt, 1, MAX_SIZE_OF_THING_NAME);
|
|
}
|
|
|
|
/*
|
|
* cfg_check_client_id() - Validate client_id in the configuration file
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_client_id(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
return cfg_check_string_length(cfg, opt, 1, MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES);
|
|
}
|
|
|
|
/*
|
|
* cfg_check_port() - Validate port is between 0 and 65535
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_port(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
return cfg_check_int_range(cfg, opt, 0, 65535);
|
|
}
|
|
|
|
/*
|
|
* cfg_check_certificates_path() - Validate certs_path value
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_certificates_path(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
DIR *dir = NULL;
|
|
char *val = cfg_opt_getnstr(opt, 0);
|
|
|
|
dir = opendir(val);
|
|
|
|
if (dir) {
|
|
closedir(dir);
|
|
return 0;
|
|
} else {
|
|
cfg_error(cfg,
|
|
"Invalid %s: cannot find directory '%s'",
|
|
opt->name, val);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* cfg_check_cert_file() - Validate certificate file name value
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_cert_file(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
char *certs_path = NULL;
|
|
char *file_path = NULL;
|
|
char *val = cfg_opt_getnstr(opt, 0);
|
|
|
|
if (cfg_check_string_length(cfg, opt, 1, 0) != 0)
|
|
return -1;
|
|
|
|
certs_path = cfg_getstr(cfg, SETTING_CERTS_PATH);
|
|
if (certs_path == NULL) {
|
|
cfg_error(cfg, "Invalid %s: NULL value", SETTING_CERTS_PATH);
|
|
return -1;
|
|
}
|
|
|
|
file_path = calloc(1, sizeof(*file_path) * (strlen(certs_path) + strlen(val) + 2));
|
|
if (file_path == NULL) {
|
|
cfg_error(cfg,
|
|
"Error checking %s: unable to allocate memory",
|
|
opt->name);
|
|
return -1;
|
|
}
|
|
sprintf(file_path, "%s/%s", certs_path, val);
|
|
|
|
if (!file_readable(file_path)) {
|
|
cfg_error(cfg,
|
|
"Invalid %s: cannot find file '%s'", opt->name, val);
|
|
free(file_path);
|
|
return -1;
|
|
}
|
|
|
|
free(file_path);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cfg_check_shadow_report_rate() - Validate shadow report rate value is between
|
|
* 1s and a year
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_shadow_report_rate(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
return cfg_check_int_range(cfg, opt,
|
|
SETTING_SHADOW_REPORT_RATE_MIN,
|
|
SETTING_SHADOW_REPORT_RATE_MAX);
|
|
}
|
|
|
|
/*
|
|
* cfg_check_temp_variation() - Validate temperature variation value is between
|
|
* 0.1 and 10.0
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_temp_variation(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
return cfg_check_float_range(cfg, opt,
|
|
SETTING_TEMP_VARIATION_MIN,
|
|
SETTING_TEMP_VARIATION_MAX);
|
|
}
|
|
|
|
/*
|
|
* cfg_check_cpuload_variation() - Validate CPU load variation value is between
|
|
* 0.1 and 50.0
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_cpuload_variation(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
return cfg_check_float_range(cfg, opt,
|
|
SETTING_CPULOAD_VARIATION_MIN,
|
|
SETTING_CPULOAD_VARIATION_MAX);
|
|
}
|
|
|
|
/*
|
|
* cfg_check_int_range() - Validate a parameter int value is between the given range
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
* @min: Minimum value of the parameter.
|
|
* @max: Maximum value of the parameter.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_int_range(cfg_t *cfg, cfg_opt_t *opt, uint32_t min, uint32_t max)
|
|
{
|
|
unsigned long val = cfg_opt_getnint(opt, 0);
|
|
|
|
if (val > max || val < min) {
|
|
cfg_error(cfg,
|
|
"Invalid %s (%d): value must be between %d and %d",
|
|
opt->name, val, min, max);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cfg_check_float_range() - Validate a parameter float value is between the given range
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
* @min: Minimum value of the parameter.
|
|
* @max: Maximum value of the parameter.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_float_range(cfg_t *cfg, cfg_opt_t *opt, float min, float max)
|
|
{
|
|
float val = cfg_opt_getnfloat(opt, 0);
|
|
|
|
if (val > max || val < min) {
|
|
cfg_error(cfg,
|
|
"Invalid %s (%f): value must be between %f and %f",
|
|
opt->name, val, min, max);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* cfg_check_empty_string() - Validate string is empty
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_empty_string(cfg_t *cfg, cfg_opt_t *opt)
|
|
{
|
|
return cfg_check_string_length(cfg, opt, 1, 0);
|
|
}
|
|
|
|
/*
|
|
* cfg_check_string_length() - Validate the length of a string is in range
|
|
*
|
|
* @cfg: The section where the option is defined.
|
|
* @opt: The option to check.
|
|
* @min: The string minimum length.
|
|
* @max: The string maximum length. 0 to unlimited.
|
|
*
|
|
* @Return: 0 on success, any other value otherwise.
|
|
*/
|
|
static int cfg_check_string_length(cfg_t *cfg, cfg_opt_t *opt, uint16_t min, uint16_t max)
|
|
{
|
|
char *val = cfg_opt_getnstr(opt, 0);
|
|
|
|
if ((val == NULL) || ((strlen(val) == 0) && (min > 0))) {
|
|
cfg_error(cfg, "Invalid %s (%s): cannot be empty", opt->name, val);
|
|
return -1;
|
|
}
|
|
if (strlen(val) < min) {
|
|
cfg_error(cfg,
|
|
"Invalid %s (%s): cannot be shorter than %d character(s)",
|
|
opt->name, val, min);
|
|
return -1;
|
|
}
|
|
if (max != 0 && strlen(val) > max) {
|
|
cfg_error(cfg,
|
|
"Invalid %s (%s): cannot be longer than %d character(s)",
|
|
opt->name, val, max);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* file_exists() - Check that the file with the given name exists
|
|
*
|
|
* @filename: Full path of the file to check if it exists.
|
|
*
|
|
* Return: 1 if the file exits, 0 if it does not exist.
|
|
*/
|
|
static int file_exists(const char *const filename)
|
|
{
|
|
return access(filename, F_OK) == 0;
|
|
}
|
|
|
|
/**
|
|
* file_readable() - Check that the file with the given name can be read
|
|
*
|
|
* @filename: Full path of the file to check if it is readable.
|
|
*
|
|
* Return: 1 if the file is readable, 0 if it cannot be read.
|
|
*/
|
|
static int file_readable(const char *const filename)
|
|
{
|
|
return access(filename, R_OK) == 0;
|
|
}
|