mirror of
https://github.com/clearlinux/clr-boot-manager.git
synced 2026-10-04 07:48:31 +00:00
lib: Add the new CbmWriter type
In order to help reduce the complexity of clr-boot-manager, much of which actually revolves around handling string and malloc failures, we now add a new CbmWriter type. Effectively, this is a memory based write stream, designed to allow failure. It is designed to allow multiple append/printf style invocations, and then only testing for memory failures when this has all been done. As such this can vastly simplify the obtuse code used to build up configuration files within clr-boot-manager (syslinux handler being a perfect example.) The style of the API usage largely follows that of the CbmMappedFile system, in attempting to avoid as many copies as possible, by using a pointer to an anonymous stack struct, and associated open/close methods. Due to the way that the memstream API works, we require both a free *and* a close. The close method is responsible for finalizing the buffer, and cleaning up the associated file resource. This in turn ensures the null terminator is placed into the final buffer. The free method is then hooked up into the autofree system to ensure we never leak. Signed-off-by: Ikey Doherty <michael.i.doherty@intel.com>
This commit is contained in:
committed by
William Douglas
parent
4da647b66c
commit
9edd1875fc
@@ -104,6 +104,8 @@ libcbm_la_SOURCES = \
|
||||
src/lib/os-release.c \
|
||||
src/lib/log.h \
|
||||
src/lib/log.c \
|
||||
src/lib/writer.h \
|
||||
src/lib/writer.c \
|
||||
src/lib/util.h
|
||||
|
||||
libcbm_la_CFLAGS = \
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file is part of clr-boot-manager.
|
||||
*
|
||||
* Copyright © 2017 Intel Corporation
|
||||
*
|
||||
* clr-boot-manager is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "writer.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
bool cbm_writer_open(CbmWriter *writer)
|
||||
{
|
||||
if (!writer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (writer->buffer || writer->memstream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
writer->memstream = open_memstream(&writer->buffer, &writer->buffer_n);
|
||||
if (!writer->memstream) {
|
||||
writer->error = ENOMEM;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void cbm_writer_free(CbmWriter *self)
|
||||
{
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
cbm_writer_close(self);
|
||||
free(self->buffer);
|
||||
}
|
||||
|
||||
void cbm_writer_close(CbmWriter *self)
|
||||
{
|
||||
if (!self) {
|
||||
return;
|
||||
}
|
||||
if (!self->memstream) {
|
||||
return;
|
||||
}
|
||||
fclose(self->memstream);
|
||||
self->memstream = NULL;
|
||||
}
|
||||
|
||||
void cbm_writer_append(CbmWriter *self, const char *s)
|
||||
{
|
||||
if (!self || self->error != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set EBADF as we tried to use a closed memstream */
|
||||
if (!self->memstream) {
|
||||
self->error = EBADF;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fprintf(self->memstream, "%s", s) < 0) {
|
||||
self->error = errno;
|
||||
}
|
||||
}
|
||||
|
||||
void cbm_writer_append_printf(CbmWriter *self, const char *fmt, ...)
|
||||
{
|
||||
if (!self || self->error != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set EBADF as we tried to use a closed memstream */
|
||||
if (!self->memstream) {
|
||||
self->error = EBADF;
|
||||
return;
|
||||
}
|
||||
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
if (vfprintf(self->memstream, fmt, va) < 0) {
|
||||
self->error = errno;
|
||||
}
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
int cbm_writer_error(CbmWriter *self)
|
||||
{
|
||||
if (self) {
|
||||
return self->error;
|
||||
}
|
||||
/* Assume enomem with pointer issues */
|
||||
return ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of clr-boot-manager.
|
||||
*
|
||||
* Copyright © 2017 Intel Corporation
|
||||
*
|
||||
* clr-boot-manager is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1
|
||||
* of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "nica/util.h"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
typedef struct CbmWriter {
|
||||
FILE *memstream;
|
||||
char *buffer;
|
||||
size_t buffer_n;
|
||||
int error;
|
||||
} CbmWriter;
|
||||
|
||||
#define CBM_WRITER_INIT &(CbmWriter){ 0 };
|
||||
|
||||
/**
|
||||
* Construct a new CbmWriter
|
||||
*/
|
||||
bool cbm_writer_open(CbmWriter *writer);
|
||||
|
||||
/**
|
||||
* Clean up a previously allocated CbmWriter
|
||||
*/
|
||||
void cbm_writer_free(CbmWriter *writer);
|
||||
|
||||
/**
|
||||
* Close the writer, which will ensure that the buffer is NULL terminated.
|
||||
* No more writes are possible after this close.
|
||||
*/
|
||||
void cbm_writer_close(CbmWriter *writer);
|
||||
|
||||
/**
|
||||
* Append string to the buffer
|
||||
*/
|
||||
void cbm_writer_append(CbmWriter *writer, const char *s);
|
||||
|
||||
/**
|
||||
* Append, printf style, to the buffer
|
||||
*/
|
||||
void cbm_writer_append_printf(CbmWriter *writer, const char *fmt, ...)
|
||||
__attribute__((format(printf, 2, 3)));
|
||||
|
||||
/**
|
||||
* Return an error that may exist in the stream, otherwise 0.
|
||||
* This allows utilising CbmWriter in a failsafe fashion, and checking the
|
||||
* error once only.
|
||||
*/
|
||||
int cbm_writer_error(CbmWriter *writer);
|
||||
|
||||
/* Convenience: Automatically clean up the CbmWriter */
|
||||
DEF_AUTOFREE(CbmWriter, cbm_writer_free)
|
||||
|
||||
/*
|
||||
* Editor modelines - https://www.wireshark.org/tools/modelines.html
|
||||
*
|
||||
* Local variables:
|
||||
* c-basic-offset: 8
|
||||
* tab-width: 8
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*
|
||||
* vi: set shiftwidth=8 tabstop=8 expandtab:
|
||||
* :indentSize=8:tabSize=8:noTabs=true:
|
||||
*/
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <check.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "nica/files.h"
|
||||
#include "util.h"
|
||||
#include "util.h"
|
||||
#include "writer.h"
|
||||
|
||||
#include "harness.h"
|
||||
|
||||
@@ -522,6 +524,68 @@ START_TEST(bootman_timeout_test)
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(bootman_writer_simple_test)
|
||||
{
|
||||
autofree(CbmWriter) *writer = CBM_WRITER_INIT;
|
||||
|
||||
fail_if(!cbm_writer_open(writer), "Failed to create writer");
|
||||
|
||||
cbm_writer_append(writer, "Bob");
|
||||
cbm_writer_append(writer, "-");
|
||||
cbm_writer_append(writer, "Jim");
|
||||
|
||||
fail_if(cbm_writer_error(writer) != 0, "Error should be 0");
|
||||
|
||||
cbm_writer_close(writer);
|
||||
fail_if(!writer->buffer, "Failed to get writer data");
|
||||
fail_if(!streq(writer->buffer, "Bob-Jim"), "Returned data is incorrect");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(bootman_writer_printf_test)
|
||||
{
|
||||
autofree(CbmWriter) *writer = CBM_WRITER_INIT;
|
||||
|
||||
fail_if(!cbm_writer_open(writer), "Failed to create writer");
|
||||
|
||||
cbm_writer_append_printf(writer, "%s = %d", "Jim", 12);
|
||||
fail_if(cbm_writer_error(writer) != 0, "Error should be 0");
|
||||
|
||||
cbm_writer_close(writer);
|
||||
fail_if(!writer->buffer, "Failed to get writer data");
|
||||
fail_if(!streq(writer->buffer, "Jim = 12"), "Returned data is incorrect");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(bootman_writer_mut_test)
|
||||
{
|
||||
autofree(CbmWriter) *writer = CBM_WRITER_INIT;
|
||||
char *data = NULL;
|
||||
|
||||
fail_if(!cbm_writer_open(writer), "Failed to create writer");
|
||||
|
||||
cbm_writer_append(writer, "One");
|
||||
cbm_writer_append(writer, "Two");
|
||||
cbm_writer_close(writer);
|
||||
data = writer->buffer;
|
||||
fail_if(!data, "Failed to get data");
|
||||
|
||||
/* Should actually result in EBADF */
|
||||
cbm_writer_append(writer, "Three");
|
||||
fail_if(cbm_writer_error(writer) == 0, "Error should be zero");
|
||||
/* The expected behaviour */
|
||||
fail_if(cbm_writer_error(writer) != EBADF, "Invalid error on closed stream");
|
||||
|
||||
cbm_writer_close(writer);
|
||||
fail_if(!writer->buffer, "Failed to get comparison");
|
||||
|
||||
/* Test mutability */
|
||||
fail_if(!streq(data, "OneTwo"), "Invalid return data");
|
||||
fail_if(writer->buffer != data, "Pointers mutated between writes");
|
||||
fail_if(!streq(data, writer->buffer), "Returned data does not match comparison data");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *core_suite(void)
|
||||
{
|
||||
Suite *s = NULL;
|
||||
@@ -547,6 +611,12 @@ static Suite *core_suite(void)
|
||||
tcase_add_test(tc, bootman_timeout_test);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
tc = tcase_create("bootman_writer_functions");
|
||||
tcase_add_test(tc, bootman_writer_simple_test);
|
||||
tcase_add_test(tc, bootman_writer_printf_test);
|
||||
tcase_add_test(tc, bootman_writer_mut_test);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user