1
0
Fork 0
mirror of https://github.com/pygos/init.git synced 2024-11-15 00:07:11 +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; } 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 The scanner reads from the provided fd. The filename is used for
cleared if end-of-file is reached. 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: The following transformations are applied:
- Space characters are replaced with regular white space characters. - 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 - If a '"' is encounterd, the above rules are disabled, until a
after the matching '"' is read. A '"' can be escaped by preceeding after the matching '"' is read. A '"' can be escaped by preceeding
it with a backslash. it with a backslash.
- If a second, coresponding '"' is not found, processing fails with - If a second, coresponding '"' is not found, processing fails.
errno set to EILSEQ.
- If a '%' character is encountered, the next character is expected - 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 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 outside the bounds set by argc, processing fails. On success,
to EINVAL. On success, the argv value is inserted and processed the argv value is inserted and processed as described above.
as described above.
- A '%' character can be escaped by writing '%%' or, if inside - A '%' character can be escaped by writing '%%' or, if inside
a double quite string, by writing \%. a double quite string, by writing \%.
- An attempt to use such an indexed argument inside an argument - 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 Remove double quotes ('"') from a string and substitute escape

View file

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

View file

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

View file

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