* apps/system/init/parser.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you 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.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <ctype.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <unistd.h>
#include "init.h"
#include "parser.h"
* Private Functions
****************************************************************************/
static int init_parse_config_lines(FAR const struct parser_s *parser,
FAR const struct parser_s **cur,
FAR size_t *line,
FAR char *buf, FAR size_t *len)
{
bool create = false;
FAR char *nl;
int ret;
while ((nl = memchr(buf, '\n', *len)))
{
*(nl++) = '\0';
*len -= nl - buf;
init_debug("line %-3zu '%s'", ++*line, buf);
if (*buf == '\0')
{
continue;
}
for (ret = 0; parser[ret].key; ret++)
{
if (!strncmp(parser[ret].key, buf, strlen(parser[ret].key)))
{
create = true;
*cur = &parser[ret];
init_debug("new section (%s)", parser[ret].key);
break;
}
}
if (!*cur)
{
return -EINVAL;
}
ret = (*cur)->parse(*cur, create, buf);
create = false;
if (ret < 0)
{
return ret;
}
memmove(buf, nl, *len);
}
return 0;
}
static int init_parse_config_buffer(FAR const struct parser_s *parser,
FAR const char *buf, size_t len)
{
char tmp[CONFIG_SYSTEM_INIT_RC_LINE_MAX];
FAR const struct parser_s *cur = NULL;
size_t line = 0;
size_t off = 0;
size_t n = 0;
size_t r;
int ret;
for (; ; )
{
r = MIN(len - off, sizeof(tmp));
memcpy(&tmp[n], &buf[off], r);
if (r == 0)
{
if (n == 0)
{
break;
}
tmp[n++] = '\n';
}
n += r;
off += r;
ret = init_parse_config_lines(parser, &cur, &line, tmp, &n);
if (ret < 0)
{
return ret;
}
if (n == sizeof(tmp))
{
return -E2BIG;
}
}
return 0;
}
* Public Functions
****************************************************************************/
int init_parse_arguments(FAR char *buf, bool dup, int argc, FAR char **argv)
{
bool quote = false;
bool new = true;
int i = 0;
for (; ; )
{
while (isblank(*buf))
{
if (!quote)
{
*buf = '\0';
new = true;
}
buf++;
}
if (*buf == '-' && *(buf + 1) == '-')
{
argv[i++] = buf;
if (i >= argc || *(buf += 2) == '\0')
{
break;
}
while (isblank(*buf))
{
*buf++ = '\0';
}
argv[i++] = buf;
break;
}
if (*buf == '\"')
{
*buf = '\0';
if (quote)
{
quote = false;
}
else
{
quote = true;
new = true;
buf++;
}
}
if (*buf == '\0')
{
break;
}
if (new)
{
argv[i++] = buf;
if (i >= argc)
{
break;
}
new = false;
}
buf++;
}
if (dup && i > 0)
{
argc = i;
for (i = 0; i < argc; i++)
{
argv[i] = strdup(argv[i]);
if (!argv[i])
{
while (i-- > 0)
{
free(argv[i]);
}
return -errno;
}
}
}
return i;
}
int init_parse_config_file(FAR const struct parser_s *parser,
FAR const char *file)
{
char buf[CONFIG_SYSTEM_INIT_RC_LINE_MAX];
FAR const struct parser_s *cur = NULL;
size_t line = 0;
size_t n = 0;
int ret = 0;
int fd;
init_debug("parsing %s", file);
fd = open(file, O_RDONLY | O_CLOEXEC);
if (fd < 0)
{
init_err("opening %s %d", file, errno);
return -errno;
}
for (; ; )
{
ssize_t r = read(fd, &buf[n], sizeof(buf) - n);
if (r < 0)
{
if (errno == EINTR)
{
continue;
}
ret = -errno;
goto out;
}
else if (r == 0)
{
if (n == 0)
{
break;
}
buf[n++] = '\n';
}
n += r;
ret = init_parse_config_lines(parser, &cur, &line, buf, &n);
if (ret < 0)
{
goto out;
}
if (n == sizeof(buf))
{
ret = -E2BIG;
goto out;
}
}
for (n = 0; parser[n].key; n++)
{
if (parser[n].check)
{
ret = parser[n].check(&parser[n]);
if (ret < 0)
{
break;
}
}
}
out:
close(fd);
if (ret < 0)
{
init_err("parse %s %d", file, ret);
}
return ret;
}
int init_parse_configs(FAR const struct parser_s *parser)
{
static FAR const char *base = "/etc/init.d/init.";
static const char preset[] =
"on boot\n"
" trigger init\n"
"on init\n"
" board_init\n"
#ifdef CONFIG_NETUTILS_NETINIT
" trigger netinit\n"
"on netinit\n"
" board_netinit\n"
#endif
#ifdef CONFIG_BOARDCTL_FINALINIT
" trigger finalinit\n"
"on finalinit\n"
" board_finalinit\n"
#endif
;
char file[PATH_MAX];
int ret;
ret = init_parse_config_buffer(parser, preset, sizeof(preset));
if (ret < 0)
{
return ret;
}
snprintf(file, sizeof(file), "%src", base);
ret = init_parse_config_file(parser, file);
if (ret < 0)
{
return ret;
}
snprintf(file, sizeof(file), "%scpu%d.rc", base, sched_getcpu());
ret = access(file, F_OK);
if (ret < 0)
{
init_debug("skipping non-exist file %s", file);
return 0;
}
return init_parse_config_file(parser, file);
}