1
0
Fork 0
mirror of https://github.com/pygos/init.git synced 2024-11-14 15:57:10 +01:00

Make line buffer static, move error reporting to rdline

- Expose rdline_t struct and move a lot of extra processing
   to rdline code
 - Make line buffer statically allocated
 - Simplify rdsvc code

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
David Oberhollenzer 2018-04-11 00:04:33 +02:00
parent a42022c650
commit 77725291ef
4 changed files with 178 additions and 170 deletions

View file

@ -36,13 +36,40 @@ typedef struct {
} enum_map_t;
typedef struct {
int fd; /* input file descriptor */
const char *argstr; /* if not NULL, read from this instead */
const char *filename; /* input file name */
size_t lineno; /* current line number */
size_t i; /* buffer offset */
char buffer[256]; /* current line, null-terminated */
int argc;
const char *const *argv;
bool string; /* inside a string? */
bool escape; /* reading an escape sequence? */
bool comment; /* inside a comment */
} rdline_t;
/*
Read from fd until end-of-file or a line feed is encountered.
Initialize the config line scanner.
Returns NULL with errno set on failure. Returns NULL with errno
cleared if end-of-file is reached.
The scanner reads from the provided fd. The filename is used for
error reporting. An argument count and vector can be set for argument
substitution in rdline.
*/
void rdline_init(rdline_t *t, int fd, const char *filename,
int argc, const char *const *argv);
The line must be deallocated with free().
/*
Read from file until end-of-file or a line feed is encountered.
Returns -1 on failure, +1 if end of file was reached,
0 if data was read successfully.
The following transformations are applied:
- Space characters are replaced with regular white space characters.
@ -53,19 +80,18 @@ typedef struct {
- If a '"' is encounterd, the above rules are disabled, until a
after the matching '"' is read. A '"' can be escaped by preceeding
it with a backslash.
- If a second, coresponding '"' is not found, processing fails with
errno set to EILSEQ.
- If a second, coresponding '"' is not found, processing fails.
- If a '%' character is encountered, the next character is expected
to be a single digit index into argv. If it is not a digit or
outside the bounds set by argc, processing fails and sets errno
to EINVAL. On success, the argv value is inserted and processed
as described above.
outside the bounds set by argc, processing fails. On success,
the argv value is inserted and processed as described above.
- A '%' character can be escaped by writing '%%' or, if inside
a double quite string, by writing \%.
- An attempt to use such an indexed argument inside an argument
expansion, results in failure with errno set to ELOOP.
expansion, results in failure.
- If the resulting line is empty, processing is restarted.
*/
char *rdline(int fd, int argc, const char *const *argv);
int rdline(rdline_t *t);
/*
Remove double quotes ('"') from a string and substitute escape

View file

@ -20,22 +20,10 @@
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <stdio.h>
#include "util.h"
typedef struct {
int fd; /* input file descriptor */
const char *argstr; /* if not NULL, read from this instead */
size_t i; /* buffer offset */
size_t bufsiz; /* buffer size */
char *buffer;
bool string; /* inside a string? */
bool escape; /* reading an escape sequence? */
bool comment; /* inside a comment */
} rdline_t;
static int rdline_getc(rdline_t *t)
{
int ret;
@ -69,9 +57,6 @@ out:
static int rdline_append(rdline_t *t, int c)
{
size_t newsz;
char *new;
if (t->comment) {
if (c != '\0')
return 0;
@ -102,67 +87,88 @@ static int rdline_append(rdline_t *t, int c)
t->i -= 1;
}
if (t->i == t->bufsiz) {
newsz = t->bufsiz ? t->bufsiz * 2 : 16;
new = realloc(t->buffer, newsz);
if (new == NULL)
return -1;
t->buffer = new;
t->bufsiz = newsz;
}
if (t->i == sizeof(t->buffer))
return -1;
t->buffer[t->i++] = c;
return 0;
}
char *rdline(int fd, int argc, const char *const *argv)
void rdline_init(rdline_t *t, int fd, const char *filename,
int argc, const char *const *argv)
{
rdline_t rd;
int c, ret;
memset(t, 0, sizeof(*t));
t->fd = fd;
t->filename = filename;
t->argc = argc;
t->argv = argv;
}
memset(&rd, 0, sizeof(rd));
rd.fd = fd;
int rdline(rdline_t *t)
{
const char *errstr;
int c;
retry:
t->i = 0;
t->argstr = NULL;
t->string = t->escape = t->comment = false;
t->lineno += 1;
do {
c = rdline_getc(&rd);
if (c < 0)
errno = 0;
c = rdline_getc(t);
if (c < 0) {
if (errno == 0)
return 1;
errstr = strerror(errno);
goto fail;
if (c == 0 && rd.string) {
errno = EILSEQ;
}
if (c == 0 && t->string) {
errstr = "missing \"";
goto fail;
}
if (c == '%') {
c = rdline_getc(&rd);
if (c == 0)
errno = EILSEQ;
if (c <= 0)
c = rdline_getc(t);
if (c == 0) {
errstr = "unexpected end of line after '%%'";
goto fail;
}
if (c < 0) {
errstr = strerror(errno);
goto fail;
}
if (c != '%') {
if (!isdigit(c) || (c - '0') >= argc) {
errno = EINVAL;
if (!isdigit(c)) {
errstr = "exptected digit after '%%'";
goto fail;
}
if (rd.argstr != NULL) {
errno = ELOOP;
if ((c - '0') >= t->argc) {
errstr = "argument out of range";
goto fail;
}
rd.argstr = argv[c - '0'];
if (t->argstr != NULL) {
errstr = "recursive argument "
"expansion";
goto fail;
}
t->argstr = t->argv[c - '0'];
continue;
}
}
if (rdline_append(&rd, c))
if (rdline_append(t, c)) {
errstr = "line too long";
goto fail;
}
} while (c != '\0');
return rd.buffer;
if (t->buffer[0] == '\0')
goto retry;
return 0;
fail:
ret = errno;
free(rd.buffer);
errno = ret;
return NULL;
fprintf(stderr, "%s: %zu: %s\n", t->filename, t->lineno, errstr);
return -1;
}

View file

@ -28,17 +28,17 @@
#include "service.h"
#include "util.h"
static int try_unescape(char *arg, const char *filename, size_t lineno)
static int try_unescape(char *arg, rdline_t *rd)
{
if (unescape(arg)) {
fprintf(stderr, "%s: %zu: malformed string constant\n",
filename, lineno);
rd->filename, rd->lineno);
return -1;
}
return 0;
}
static char **try_split_argv(char *str, const char *filename, size_t lineno)
static char **try_split_argv(char *str, rdline_t *rd)
{
char **argv = split_argv(str);
@ -46,11 +46,11 @@ static char **try_split_argv(char *str, const char *filename, size_t lineno)
switch (errno) {
case EINVAL:
fprintf(stderr, "%s: %zu: malformed string constant\n",
filename, lineno);
rd->filename, rd->lineno);
break;
default:
fprintf(stderr, "%s: %zu: %s\n", filename, lineno,
strerror(errno));
fprintf(stderr, "%s: %zu: %s\n",
rd->filename, rd->lineno, strerror(errno));
break;
}
}
@ -58,43 +58,56 @@ static char **try_split_argv(char *str, const char *filename, size_t lineno)
return argv;
}
static int svc_desc(service_t *svc, char *arg,
const char *filename, size_t lineno)
static char *try_strdup(const char *str, rdline_t *rd)
{
if (try_unescape(arg, filename, lineno))
return -1;
svc->desc = arg;
return 0;
char *out = strdup(str);
if (out == NULL) {
fprintf(stderr, "%s: %zu: out of memory\n",
rd->filename, rd->lineno);
}
return out;
}
static int svc_tty(service_t *svc, char *arg,
const char *filename, size_t lineno)
static int svc_desc(service_t *svc, char *arg, rdline_t *rd)
{
if (try_unescape(arg, filename, lineno))
if (try_unescape(arg, rd))
return -1;
svc->ctty = arg;
return 0;
svc->desc = try_strdup(arg, rd);
return svc->desc == NULL ? -1 : 0;
}
static int svc_exec(service_t *svc, char *arg,
const char *filename, size_t lineno)
static int svc_tty(service_t *svc, char *arg, rdline_t *rd)
{
if (try_unescape(arg, rd))
return -1;
svc->ctty = try_strdup(arg, rd);
return svc->ctty == NULL ? -1 : 0;
}
static int svc_exec(service_t *svc, char *arg, rdline_t *rd)
{
exec_t *e, *end;
char **argv;
argv = try_split_argv(arg, filename, lineno);
if (argv == NULL)
return -1;
e = calloc(1, sizeof(*e));
if (e == NULL) {
fprintf(stderr, "%s: %zu: out of memory\n", filename, lineno);
free(argv);
fprintf(stderr, "%s: %zu: out of memory\n",
rd->filename, rd->lineno);
return -1;
}
e->argv = argv;
e->raw_argv = arg;
e->raw_argv = try_strdup(arg, rd);
if (e->raw_argv == NULL) {
free(e);
return -1;
}
e->argv = try_split_argv(e->raw_argv, rd);
if (e->argv == NULL) {
free(e->raw_argv);
free(e);
return -1;
}
if (svc->exec == NULL) {
svc->exec = e;
@ -106,47 +119,50 @@ static int svc_exec(service_t *svc, char *arg,
return 0;
}
static int svc_before(service_t *svc, char *arg,
const char *filename, size_t lineno)
static int svc_before(service_t *svc, char *arg, rdline_t *rd)
{
if (svc->before != NULL) {
fprintf(stderr, "%s: %zu: 'before' dependencies respecified\n",
filename, lineno);
rd->filename, rd->lineno);
return -1;
}
svc->before = try_split_argv(arg, filename, lineno);
svc->raw_before = try_strdup(arg, rd);
if (svc->raw_before == NULL)
return -1;
svc->before = try_split_argv(svc->raw_before, rd);
if (svc->before == NULL)
return -1;
svc->raw_before = arg;
return 0;
}
static int svc_after(service_t *svc, char *arg,
const char *filename, size_t lineno)
static int svc_after(service_t *svc, char *arg, rdline_t *rd)
{
if (svc->after != NULL) {
fprintf(stderr, "%s: %zu: 'after' dependencies respecified\n",
filename, lineno);
rd->filename, rd->lineno);
return -1;
}
svc->after = try_split_argv(arg, filename, lineno);
svc->raw_after = try_strdup(arg, rd);
if (svc->raw_after == NULL)
return -1;
svc->after = try_split_argv(svc->raw_after, rd);
if (svc->after == NULL)
return -1;
svc->raw_after = arg;
return 0;
}
static int svc_type(service_t *svc, char *arg,
const char *filename, size_t lineno)
static int svc_type(service_t *svc, char *arg, rdline_t *rd)
{
char **args;
int i, type;
args = try_split_argv(arg, filename, lineno);
args = try_split_argv(arg, rd);
if (args == NULL)
return -1;
@ -155,7 +171,7 @@ static int svc_type(service_t *svc, char *arg,
if (type == -1) {
fprintf(stderr, "%s: %zu: unknown service type '%s'\n",
filename, lineno, args[0]);
rd->filename, rd->lineno, args[0]);
free(args);
return -1;
}
@ -183,48 +199,45 @@ static int svc_type(service_t *svc, char *arg,
/* fall-through */
default:
fprintf(stderr, "%s: %zu: unexpected extra arguments "
"for type '%s'\n", filename, lineno, arg);
"for type '%s'\n",
rd->filename, rd->lineno, arg);
return -1;
}
}
svc->type = type;
free(args);
free(arg);
return 0;
fail_limit:
fprintf(stderr, "%s: %zu: expected 'limit <value>' after 'respawn'\n",
filename, lineno);
rd->filename, rd->lineno);
free(args);
return -1;
}
static int svc_target(service_t *svc, char *arg,
const char *filename, size_t lineno)
static int svc_target(service_t *svc, char *arg, rdline_t *rd)
{
int target;
if (try_unescape(arg, filename, lineno))
if (try_unescape(arg, rd))
return -1;
target = svc_target_from_string(arg);
if (target == -1) {
fprintf(stderr, "%s: %zu: unknown service target '%s'\n",
filename, lineno, arg);
rd->filename, rd->lineno, arg);
return -1;
}
svc->target = target;
free(arg);
return 0;
}
static const struct svc_param {
const char *key;
int (*handle)(service_t *svc, char *arg,
const char *filename, size_t lineno);
int (*handle)(service_t *svc, char *arg, rdline_t *rd);
} svc_params[] = {
{ "description", svc_desc },
{ "exec", svc_exec },
@ -235,49 +248,15 @@ static const struct svc_param {
{ "after", svc_after },
};
static char *get_line(int fd, const char *filename, size_t *lineno,
int argc, const char *const *args)
static int splitkv(rdline_t *rd, char **k, char **v)
{
const char *error;
char *line;
int ret;
retry:
errno = 0;
line = rdline(fd, argc, args);
ret = errno;
if (line == NULL && errno != 0) {
switch (errno) {
case EINVAL: error = "error in argument expansion"; break;
case ELOOP: error = "recursive argument expansion"; break;
case EILSEQ: error = "missing \""; break;
default: error = strerror(errno); break;
}
fprintf(stderr, "%s: %zu: %s\n", filename, *lineno, error);
}
if (line != NULL && strlen(line) == 0) {
free(line);
(*lineno) += 1;
goto retry;
}
errno = ret;
return line;
}
static int splitkv(const char *filename, size_t lineno,
char *line, char **k, char **v)
{
char *key = line, *value = line;
char *key = rd->buffer, *value = rd->buffer;
while (*value != ' ' && *value != '\0') {
if (!isalpha(*value)) {
fprintf(stderr,
"%s: %zu: unexpected '%c' in keyword\n",
filename, lineno, *value);
rd->filename, rd->lineno, *value);
return -1;
}
++value;
@ -285,7 +264,7 @@ static int splitkv(const char *filename, size_t lineno,
if (*value != ' ') {
fprintf(stderr, "%s: %zu: expected argument after '%s'\n",
filename, lineno, key);
rd->filename, rd->lineno, key);
return -1;
}
@ -296,8 +275,7 @@ static int splitkv(const char *filename, size_t lineno,
return 0;
}
static const struct svc_param *find_param(const char *filename, size_t lineno,
const char *name)
static const struct svc_param *find_param(rdline_t *rd, const char *name)
{
size_t i;
@ -307,19 +285,20 @@ static const struct svc_param *find_param(const char *filename, size_t lineno,
}
fprintf(stderr, "%s: %zu: unknown keyword '%s'\n",
filename, lineno, name);
rd->filename, rd->lineno, name);
return NULL;
}
service_t *rdsvc(int dirfd, const char *filename)
{
char *line = NULL, *key, *value;
const struct svc_param *p;
const char *arg, *args[1];
service_t *svc = NULL;
size_t argc, lineno;
int fd;
char *key, *value;
size_t argc;
rdline_t rd;
int fd, ret;
fd = openat(dirfd, filename, O_RDONLY);
if (fd < 0) {
@ -335,6 +314,8 @@ service_t *rdsvc(int dirfd, const char *filename)
argc = 0;
}
rdline_init(&rd, fd, filename, argc, args);
svc = calloc(1, sizeof(*svc));
if (svc == NULL)
goto fail_oom;
@ -348,32 +329,26 @@ service_t *rdsvc(int dirfd, const char *filename)
if (svc->name == NULL)
goto fail_oom;
for (lineno = 1; ; ++lineno) {
line = get_line(fd, filename, &lineno, argc, args);
if (line == NULL) {
if (errno == 0)
break;
goto fail;
}
if (splitkv(filename, lineno, line, &key, &value))
while ((ret = rdline(&rd)) == 0) {
if (splitkv(&rd, &key, &value))
goto fail;
p = find_param(filename, lineno, key);
p = find_param(&rd, key);
if (p == NULL)
goto fail;
memmove(line, value, strlen(value) + 1);
if (p->handle(svc, line, filename, lineno))
if (p->handle(svc, value, &rd))
goto fail;
}
if (ret < 0)
goto fail;
close(fd);
return svc;
fail_oom:
fputs("out of memory\n", stderr);
fail:
free(line);
delsvc(svc);
close(fd);
return NULL;

View file

@ -59,6 +59,7 @@ int unescape(char *src)
case 't': c = '\t'; break;
case '\\':
case '"':
case '%':
break;
case 'x':
c = 0;