summaryrefslogtreecommitdiffstats
path: root/src/tools/common/sss_colondb.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/common/sss_colondb.c')
-rw-r--r--src/tools/common/sss_colondb.c317
1 files changed, 317 insertions, 0 deletions
diff --git a/src/tools/common/sss_colondb.c b/src/tools/common/sss_colondb.c
new file mode 100644
index 0000000..41e6c3a
--- /dev/null
+++ b/src/tools/common/sss_colondb.c
@@ -0,0 +1,317 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2015 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+
+#include "util/util.h"
+#include "util/strtonum.h"
+#include "tools/common/sss_colondb.h"
+
+#define IS_STD_FILE(db) ((db)->file == stdin || (db)->file == stdout)
+
+static char *read_field_as_string(char *line,
+ const char **_value)
+{
+ char *rest;
+ char *value;
+
+ if (line == NULL || *line == '\n' || *line == '\0') {
+ /* There is nothing else to read. */
+ rest = NULL;
+ value = NULL;
+ goto done;
+ }
+
+ if (*line == ':') {
+ /* Special case for empty value. */
+ *line = '\0';
+ rest = line + 1;
+ value = NULL;
+ goto done;
+ }
+
+ /* Value starts at current position. */
+ value = line;
+
+ /* Find next field delimiter. */
+ rest = strchr(line, ':');
+ if (rest == NULL) {
+ /* There is no more field. Remove \n from the end. */
+ rest = strchr(line, '\n');
+ if (rest != NULL) {
+ *rest = '\0';
+ rest = NULL;
+ }
+ goto done;
+ }
+
+ /* Remove it and step one character further. */
+ *rest = '\0';
+ rest++;
+
+done:
+ *_value = value;
+
+ return rest;
+}
+
+static char *read_field_as_uint32(char *line,
+ uint32_t *_value)
+{
+ const char *str;
+ char *rest;
+ errno_t ret;
+ char *endptr;
+
+ rest = read_field_as_string(line, &str);
+ if (str == NULL) {
+ *_value = 0;
+ return rest;
+ }
+
+ *_value = strtouint32(str, &endptr, 10);
+ if ((errno != 0) || *endptr || (str == endptr)) {
+ ret = errno ? errno : EINVAL;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse number [%d]: %s\n",
+ ret, sss_strerror(ret));
+
+ *_value = 0;
+ }
+
+ return rest;
+}
+
+struct sss_colondb {
+ FILE *file;
+ enum sss_colondb_mode mode;
+};
+
+errno_t sss_colondb_readline(TALLOC_CTX *mem_ctx,
+ struct sss_colondb *db,
+ struct sss_colondb_read_field *table)
+{
+ int readchars;
+ size_t linelen = 0;
+ char *line = NULL;
+ char *tcline;
+ char *rest;
+ errno_t ret;
+ int i;
+
+ if (db->mode != SSS_COLONDB_READ) {
+ return ERR_INTERNAL;
+ }
+
+ readchars = getline(&line, &linelen, db->file);
+ if (readchars == -1) {
+ /* Nothing was read. */
+
+ free(line);
+ line = NULL;
+
+ if (errno != 0) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to read line [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ return EOF;
+ }
+
+ /* Copy line to mem_ctx. */
+ tcline = talloc_strdup(mem_ctx, line);
+
+ free(line);
+ line = NULL;
+
+ if (tcline == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
+ return ENOMEM;
+ }
+
+ rest = tcline;
+ for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) {
+ switch (table[i].type) {
+ case SSS_COLONDB_UINT32:
+ rest = read_field_as_uint32(rest, table[i].data.uint32);
+ break;
+ case SSS_COLONDB_STRING:
+ rest = read_field_as_string(rest, table[i].data.str);
+ break;
+ case SSS_COLONDB_SENTINEL:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (rest == NULL && table[i + 1].type != SSS_COLONDB_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Line contains less values than expected!\n");
+ ret = EINVAL;
+ goto done;
+ } else if (rest != NULL && table[i + 1].type == SSS_COLONDB_SENTINEL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Line contains more values than expected!\n");
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(tcline);
+ }
+
+ return ret;
+}
+
+errno_t sss_colondb_writeline(struct sss_colondb *db,
+ struct sss_colondb_write_field *table)
+{
+ TALLOC_CTX *tmp_ctx;
+ char *line = NULL;
+ errno_t ret;
+ int i;
+
+ if (db->mode != SSS_COLONDB_WRITE) {
+ return ERR_INTERNAL;
+ }
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ line = talloc_strdup(tmp_ctx, "");
+ if (line == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; table[i].type != SSS_COLONDB_SENTINEL; i++) {
+ switch (table[i].type) {
+ case SSS_COLONDB_UINT32:
+ if (table[i].data.uint32 == 0) {
+ line = talloc_asprintf_append(line, ":");
+ } else {
+ line = talloc_asprintf_append(line, ":%u", table[i].data.uint32);
+ }
+ break;
+ case SSS_COLONDB_STRING:
+ if (table[i].data.str == NULL) {
+ line = talloc_asprintf_append(line, ":");
+ } else {
+ line = talloc_asprintf_append(line, ":%s", table[i].data.str);
+ }
+ break;
+ case SSS_COLONDB_SENTINEL:
+ DEBUG(SSSDBG_CRIT_FAILURE, "Trying to process sentinel?!\n");
+ ret = ERR_INTERNAL;
+ goto done;
+ }
+
+ if (line == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+
+ /* Remove starting : */
+ line++;
+
+ fprintf(db->file, "%s\n", line);
+ fflush(db->file);
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+static int sss_colondb_close(void *pvt)
+{
+ struct sss_colondb *db = talloc_get_type(pvt, struct sss_colondb);
+
+ if (db->file == NULL || IS_STD_FILE(db)) {
+ return 0;
+ }
+
+ fclose(db->file);
+ db->file = NULL;
+
+ return 0;
+}
+
+static FILE *open_db(const char *filename, enum sss_colondb_mode mode)
+{
+ FILE *fp = NULL;
+ errno_t ret;
+
+ errno = 0;
+
+ switch (mode) {
+ case SSS_COLONDB_READ:
+ fp = filename == NULL ? stdin : fopen(filename, "r");
+ break;
+ case SSS_COLONDB_WRITE:
+ fp = filename == NULL ? stdout : fopen(filename, "w");
+ break;
+ }
+
+ if (fp == NULL && filename != NULL) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to open file %s [%d]: %s\n",
+ filename, ret, sss_strerror(ret));
+ }
+
+ return fp;
+}
+
+struct sss_colondb *sss_colondb_open(TALLOC_CTX *mem_ctx,
+ enum sss_colondb_mode mode,
+ const char *filename)
+{
+ struct sss_colondb *db;
+
+ db = talloc_zero(mem_ctx, struct sss_colondb);
+ if (db == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero() failed\n");
+ return NULL;
+ }
+
+ db->file = open_db(filename, mode);
+ db->mode = mode;
+
+ if (db->file == NULL) {
+ talloc_free(db);
+ return NULL;
+ }
+
+ talloc_set_destructor((TALLOC_CTX *)db, sss_colondb_close);
+
+ return db;
+}