mirror of
https://github.com/pygos/init.git
synced 2024-11-25 04:20:42 +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:
parent
a42022c650
commit
77725291ef
4 changed files with 178 additions and 170 deletions
|
@ -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
|
||||
|
|
106
lib/src/rdline.c
106
lib/src/rdline.c
|
@ -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;
|
||||
}
|
||||
|
|
193
lib/src/rdsvc.c
193
lib/src/rdsvc.c
|
@ -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;
|
||||
|
|
|
@ -59,6 +59,7 @@ int unescape(char *src)
|
|||
case 't': c = '\t'; break;
|
||||
case '\\':
|
||||
case '"':
|
||||
case '%':
|
||||
break;
|
||||
case 'x':
|
||||
c = 0;
|
||||
|
|
Loading…
Reference in a new issue