Dmitri Pal wrote: > Stephen Gallagher wrote: > >> On 09/28/2009 01:46 PM, Dmitri Pal wrote: >> >> >>> Stephen Gallagher wrote: >>> >>> >>>> On 09/28/2009 09:55 AM, Simo Sorce wrote: >>>> >>>> >>>> >>>>> On Mon, 2009-09-28 at 09:38 -0400, Stephen Gallagher wrote: >>>>> >>>>> >>>>> >>>>>> The SSSD needs a config_from_fd() variant of the config_from_file() >>>>>> call >>>>>> so that we can preopen a config file and perform some verification on >>>>>> it >>>>>> before parsing it. The config_from_fd() call is used to avoid race >>>>>> conditions between testing the file and reading it in. >>>>>> >>>>>> Note: the *_from_fd() functions still require the config file name for >>>>>> internal information. This does not imply that it is used to open the >>>>>> file. >>>>>> >>>>>> >>>>>> >>>>> I think it is better not to require a file name, and, internally, just >>>>> use something like "dummy" or a random string like the process pid etc.. >>>>> >>>>> This way there is no risk that someone may accidentally change the code >>>>> later to re-open the file or something like that, if that is done it >>>>> will immediately break when it tries to open "dummy" (hopefully :-) >>>>> >>>>> Simo. >>>>> >>>>> _______________________________________________ >>>>> sssd-devel mailing list >>>>> sssd-devel@lists.fedorahosted.org >>>>> https://fedorahosted.org/mailman/listinfo/sssd-devel >>>>> >>>>> >>>>> >>>> New version does not require the file name for the _from_fd() functions. >>>> It will create a string "dummy_<fd>" to use for the config file name >>>> internally. >>>> >>>> >>>> >>>> >>>> ------------------------------------------------------------------------ >>>> >>>> _______________________________________________ >>>> sssd-devel mailing list >>>> sssd-devel@lists.fedorahosted.org >>>> https://fedorahosted.org/mailman/listinfo/sssd-devel >>>> >>>> >>> Nack: >>> >>> Had an IRC conversation with Steven: >>> >>> <dpal> sgallagh, why do you need "dummy" thing >>> <dpal> sgallagh, I would have done it differently >>> <dpal> I mean the internal layers >>> <sgallagh> dpal: I added the "dummy" thing so that there was always a >>> unique identifier at the top level of the collection >>> <sgallagh> dpal: If you would like to recommend an alternative approach, >>> please nack the patch and provide suggestions. I'm all ears :) >>> <dpal> sgallagh, then this is something that the app should pass in >>> together with fd >>> <dpal> if the app opened the file it should say how to name it >>> <sgallagh> dpal: Look at the history, I did that at first and it was nacked. >>> <sgallagh> s/history/email thread/ >>> <dpal> I have seen Simo's comment but I do not think we are talking >>> about same thing >>> <sgallagh> dpal: Perhaps you can clarify, then? >>> <dpal> The filename is used in the low level function in only one place >>> besides the opening the file - to name the collection >>> <sgallagh> right >>> <sgallagh> So I just gave it the name "dummy_fd" if it was opened as an >>> fd instead of directly opening a file path. >>> <dpal> It is done in the error list only >>> <dpal> It is done for error reporting purpose >>> <dpal> You do not want to return error list back to caller with text >>> "errors parsing dymmy_123" >>> <dpal> sgallagh, the only value of having the file name in the error >>> list is to report back the error in the file. >>> * sgallagh nods >>> <dpal> sgallagh, If the caller handles the file it should name it or >>> there should be no name in this case >>> <sgallagh> Well, that seems like a convincing-enough argument to negate >>> simo's nack, honestly. >>> <dpal> and a generic name instead >>> <sgallagh> Neither of us were really sure if the name was ever used, or >>> if it was just there to be available to the caller >>> <sgallagh> dpal: Do you want me to just change it from "dummy_<fd>" to >>> "file descriptor <fd>" instead? >>> <dpal> sgallagh, more of then I would always pass two things into >>> ini_to_collection >>> <sgallagh> dpal: Or put back the interface to let it be specified >>> <dpal> sgallagh, let me finish >>> <dpal> sgallagh, move the fopen/fdopen out of the ini_to_collection >>> <dpal> Pass in the ready to use FILE *file and the string for naming the >>> "source" >>> <dpal> Then wrap the new implementation of the function with function >>> that just open the file and sends down the filename as a source string. >>> <dpal> The new "other" function would instead do the fdopen using passed >>> in file descriptor and will send down passed in string to name the source >>> <sgallagh> dpal: That still doesn't explain the fd case. If we're >>> calling config_from_fd(), are you saying that we need to also pass the >>> filename? >>> <dpal> sgallagh, I say you need to name the source in some way so that >>> you can report the error >>> <dpal> sgallagh, it was logical to use filename as name of the source in >>> case of file >>> <dpal> It is not clear to me what would be the best name of the source >>> if you are using a FD >>> <sgallagh> dpal: I think I'd prefer to leave the interface as-is and >>> change the "dummy_fd" to the more readable "file descriptor fd" >>> <sgallagh> dpal: I'll make the changes under the hood as you've suggested >>> <dpal> This does not help the caller >>> <sgallagh> dpal: what do you mean? >>> <dpal> He does not know what this fd means >>> <sgallagh> dpal: He has to. He's the one who passed it in >>> <dpal> sgallagh, I am talking about admin who reads the output and does >>> not have a clue what fd 23 meant. >>> <sgallagh> dpal: Ok, put this all in a nack email and I'll put it back >>> the way I had it before, where you have to pass the filename as well as >>> the fd into config_from_fd(). Ok? >>> <sgallagh> I'll make the other internal changes as well >>> <dpal> If you tell him "configuration port" he will understand that the >>> configuration was read on the port. >>> <dpal> Also I would suggest not to name it as file name in this case but >>> rather "configuration_source" or something like to make it more generic >>> for fd and file cases. >>> >>> >>> >>> >> New patch attached that should address Dmitri's concerns. >> >> >> ------------------------------------------------------------------------ >> >> _______________________________________________ >> sssd-devel mailing list >> sssd-devel@lists.fedorahosted.org >> https://fedorahosted.org/mailman/listinfo/sssd-devel >> > Sorry. Nack again: > > 1) In ini_to_collection() > > if (file == NULL) { > error = errno; > TRACE_ERROR_NUMBER("Failed to open file - but this is OK", error); > return ENOENT; > } > > The errno is irrelevant and the error string needs to change. > > 2) The ini_to_collection argument should be configuration source, not > "filename". > > 3) Trace stament here should be TRACE_ERROR... instead of TRACE_FLOW... > if(!config_file) { > error = errno; > TRACE_FLOW_NUMBER("config_from_file_with_lines. Returns", error); > return error; > } > I usually have some more descriptive message in case of error. Like > "Failed to open file" or similar. > Trace FLOW is used for normal entry and exit into the function. In case > of errors it should be TRACE_ERROR... > Same comment regarding this piece of code: > > config_file = fdopen(fd, "r"); > if (!config_file) { > error = errno; > TRACE_FLOW_NUMBER("config_from_fd_with_lines. Returns", error); > return error; > } > > 4) Code inside config_for_app() function is really broken. > It was relying on the fact that in the original implementation the > failure to open the file would translate into ENOENT error code. > As you might see in this case in error processing the corresponding > action was taken. Now the error handling can't rely on this and the > whole logic about > trying one file and then another and then checking if we failed with > ENOENT with both files is broken. > > The simplest way to fix this issue is to have a wrapper that would do > something like config_from_file_with_lines() does but would open the > file and call > ini_to_collection directly. It should return ENOENT if the file opening > failed. In this case in config_for_app() you would just replace direct call > to ini_to_collection() with this function. It would be easier than to > rewrite and retest the logic of this function. > It took me some time to write it and make it work the way I wanted and > make sure it works in different scenarios. > > Refined patch attached.
-- Thank you, Dmitri Pal Engineering Manager IPA project, Red Hat Inc. ------------------------------- Looking to carve out IT costs? www.redhat.com/carveoutcosts/
>From 8e3fd20bd6cea8f56b68f7a57c252b408a9baa9c Mon Sep 17 00:00:00 2001 From: Stephen Gallagher <sgall...@redhat.com> Date: Mon, 28 Sep 2009 09:36:00 -0400 Subject: [PATCH] INI Add config_from_fd() to ini_config Patch adds ability to read configuration using already open file descriptor. Started by Steve G and refined a bit by me. --- common/ini/ini_config.c | 164 ++++++++++++++++++++++++++++++++++++++------ common/ini/ini_config.h | 23 ++++++- common/ini/ini_config_ut.c | 74 ++++++++++++++++++++ 3 files changed, 239 insertions(+), 22 deletions(-) diff --git a/common/ini/ini_config.c b/common/ini/ini_config.c index ffc5960..bd4fa79 100644 --- a/common/ini/ini_config.c +++ b/common/ini/ini_config.c @@ -27,6 +27,7 @@ #include <ctype.h> #include <stdlib.h> #include <locale.h> +#include <fcntl.h> #include "config.h" /* For error text */ #include <libintl.h> @@ -75,6 +76,17 @@ #define INI_ERROR "errors" #define INI_ERROR_NAME "errname" +/* Internally used functions */ +static int config_with_lines(const char *application, + FILE *config_file, + const char *config_source, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_list, + struct collection_item **lines); + + + /* Different error string functions can be passed as callbacks */ typedef const char * (*error_fn)(int error); @@ -153,13 +165,13 @@ int read_line(FILE *file, /***************************************************************************/ /* Function to read single ini file and pupulate * the provided collection with subcollcetions from the file */ -static int ini_to_collection(const char *filename, +static int ini_to_collection(FILE *file, + const char *config_filename, struct collection_item *ini_config, int error_level, struct collection_item **error_list, struct collection_item **lines) { - FILE *file; int error; int status; int section_count = 0; @@ -176,12 +188,9 @@ static int ini_to_collection(const char *filename, TRACE_FLOW_STRING("ini_to_collection", "Entry"); - /* Open file for reading */ - file = fopen(filename, "r"); if (file == NULL) { - error = errno; - TRACE_ERROR_NUMBER("Failed to open file - but this is OK", error); - return ENOENT; + TRACE_ERROR_NUMBER("No file handle", EINVAL); + return EINVAL; } /* Open the collection of errors */ @@ -194,7 +203,7 @@ static int ini_to_collection(const char *filename, return error; } /* Add file name as the first item */ - error = col_add_str_property(*error_list, NULL, INI_ERROR_NAME, filename, 0); + error = col_add_str_property(*error_list, NULL, INI_ERROR_NAME, config_filename, 0); if (error) { TRACE_ERROR_NUMBER("Failed to and name to collection", error); fclose(file); @@ -423,9 +432,10 @@ void free_ini_config_lines(struct collection_item *lines) TRACE_FLOW_STRING("free_ini_config_lines", "Exit"); } + /* Read configuration information from a file */ int config_from_file(const char *application, - const char *config_file, + const char *config_filename, struct collection_item **ini_config, int error_level, struct collection_item **error_list) @@ -434,7 +444,7 @@ int config_from_file(const char *application, TRACE_FLOW_STRING("config_from_file", "Entry"); error = config_from_file_with_lines(application, - config_file, + config_filename, ini_config, error_level, error_list, @@ -443,16 +453,97 @@ int config_from_file(const char *application, return error; } +/* Read configuration information from a file descriptor */ +int config_from_fd(const char *application, + int fd, + const char *config_source, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_list) +{ + int error; + + TRACE_FLOW_STRING("config_from_fd", "Entry"); + error = config_from_fd_with_lines(application, fd, config_source, + ini_config, error_level, + error_list, NULL); + TRACE_FLOW_NUMBER("config_from_fd. Returns", error); + return error; +} + /* Function to read the ini file and have a collection * of which item appers on which line */ int config_from_file_with_lines(const char *application, - const char *config_file, + const char *config_filename, struct collection_item **ini_config, int error_level, struct collection_item **error_list, struct collection_item **lines) { + int error = EOK; + FILE *config_file = NULL; + + TRACE_FLOW_STRING("config_from_file_with_lines", "Entry"); + + config_file = fopen(config_filename, "r"); + if(!config_file) { + error = errno; + TRACE_ERROR_NUMBER("Failed to open file", error); + return error; + } + + error = config_with_lines(application, config_file, + config_filename, ini_config, + error_level, error_list, + lines); + TRACE_FLOW_NUMBER("config_from_file_with_lines. Returns", error); + return error; +} + +/* Function to read the ini file and have a collection + * of which item appers on which line + */ +int config_from_fd_with_lines(const char *application, + int fd, + const char *config_source, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_list, + struct collection_item **lines) +{ + int error = EOK; + FILE *config_file; + + TRACE_FLOW_STRING("config_from_fd_with_lines", "Entry"); + + config_file = fdopen(fd, "r"); + if (!config_file) { + error = errno; + TRACE_ERROR_NUMBER("Failed to dup file", error); + return error; + } + + error = config_with_lines(application, config_file, + config_source, ini_config, + error_level, error_list, + lines); + TRACE_FLOW_NUMBER("config_from_fd_with_lines. Returns", error); + + return error; +} + +/* Low level function that prepares the collection + * and calls parser. + */ +static int config_with_lines(const char *application, + FILE *config_file, + const char *config_source, + struct collection_item **ini_config, + int error_level, + struct collection_item **error_list, + struct collection_item **lines) +{ int error; int created = 0; int created_lines = 0; @@ -511,8 +602,9 @@ int config_from_file_with_lines(const char *application, } /* Do the actual work */ - error = ini_to_collection(config_file, *ini_config, - error_level, error_list, lines); + error = ini_to_collection(config_file, config_source, + *ini_config, error_level, + error_list, lines); /* In case of error when we created collection - delete it */ if (error && created) { col_destroy_collection(*ini_config); @@ -528,6 +620,39 @@ int config_from_file_with_lines(const char *application, return error; } +/* Special wrapper around the inernal parser + * to open the file first. + * Used in conf_for_app function. + */ +static int ini_to_col_from_file(const char *config_filename, + struct collection_item *ini_config, + int error_level, + struct collection_item **error_list, + struct collection_item **lines) +{ + int error = EOK; + FILE *config_file = NULL; + + TRACE_FLOW_STRING("ini_to_col_from_file", "Entry"); + + config_file = fopen(config_filename, "r"); + if(!config_file) { + error = errno; + TRACE_ERROR_NUMBER("ini_to_col_from_file. Returns", error); + return ENOENT; + } + + error = ini_to_collection(config_file, + config_filename, + ini_config, + error_level, + error_list, + lines); + TRACE_FLOW_NUMBER("ini_to_col_from_file. Returns", error); + return error; +} + + /* Read default config file and then overwrite it with a specific one * from the directory */ int config_for_app(const char *application, @@ -605,8 +730,8 @@ int config_for_app(const char *application, /* Read master file */ if (config_file != NULL) { TRACE_INFO_STRING("Reading master file:", config_file); - error = ini_to_collection(config_file, *ini_config, - error_level, pass_common, NULL); + error = ini_to_col_from_file(config_file, *ini_config, + error_level, pass_common, NULL); tried++; /* ENOENT and EOK are Ok */ if (error) { @@ -624,7 +749,7 @@ int config_for_app(const char *application, } /* Add error results if any to the overarching error collection */ if ((pass_common != NULL) && (*pass_common != NULL)) { - TRACE_INFO_STRING("Process erros resulting from file:", config_file); + TRACE_INFO_STRING("Process errors resulting from file:", config_file); error = col_add_collection_to_collection(*error_set, NULL, NULL, *pass_common, COL_ADD_MODE_EMBED); @@ -663,10 +788,9 @@ int config_for_app(const char *application, sprintf(file_name, "%s%s%s.conf", config_dir, SLASH, application); TRACE_INFO_STRING("Opening file:", file_name); - - /* Read master file */ - error = ini_to_collection(file_name, *ini_config, - error_level, pass_specific, NULL); + /* Read specific file */ + error = ini_to_col_from_file(file_name, *ini_config, + error_level, pass_specific, NULL); tried++; free(file_name); /* ENOENT and EOK are Ok */ diff --git a/common/ini/ini_config.h b/common/ini/ini_config.h index 1fee48a..5ae4981 100644 --- a/common/ini/ini_config.h +++ b/common/ini/ini_config.h @@ -97,23 +97,42 @@ const char *validation_error_str(int parsing_error); /* Read configuration information from a file */ int config_from_file(const char *application, /* Name of the application - will be used as name of the collection */ - const char *config_file, /* Name of the config file - if NULL the collection will be empty */ + const char *config_filename, /* Name of the config file - if NULL the collection will be empty */ struct collection_item **ini_config, /* If *ini_config is NULL a new ini object will be allocated, */ /* otherwise the one that is pointed to will be updated. */ int error_level, /* Error level - break for errors, warnings or best effort (don't break) */ struct collection_item **error_list); /* List of errors for a file */ +/* Read configuration information from a file descriptor */ +int config_from_fd(const char *application, /* Name of the application - will be used as name of the collection */ + int fd, /* Previously opened file descriptor for the config file */ + const char *config_source, /* Name of the file being parsed, for use when printing the error list */ + struct collection_item **ini_config, /* If *ini_config is NULL a new ini object will be allocated*/ + int error_level, /* Error level - break for errors, warnings or best effort (don't break) */ + struct collection_item **error_list); /* List of errors for a file */ + /* Read configuration information from a file with extra collection of line numbers */ int config_from_file_with_lines( const char *application, /* Name of the application - will be used as name of the collection */ - const char *config_file, /* Name of the config file - if NULL the collection will be empty */ + const char *config_filename, /* Name of the config file - if NULL the collection will be empty */ struct collection_item **ini_config, /* If *ini_config is NULL a new ini object will be allocated, */ /* otherwise the one that is pointed to will be updated. */ int error_level, /* Error level - break for errors, warnings or best effort (don't break) */ struct collection_item **error_list, /* List of errors for a file */ struct collection_item **lines); /* Collection of pairs where key is the key and value is line number */ +/* Read configuration information from a file descriptor with extra collection of line numbers */ +int config_from_fd_with_lines( + const char *application, /* Name of the application - will be used as name of the collection */ + int fd, /* Previously opened file descriptor for the config file */ + const char *config_source, /* Name of the file being parsed, for use when printing the error list */ + struct collection_item **ini_config, /* If *ini_config is NULL a new ini object will be allocated, */ + /* otherwise the one that is pointed to will be updated. */ + int error_level, /* Error level - break for errors, warnings or best effort (don't break) */ + struct collection_item **error_list, /* List of errors for a file */ + struct collection_item **lines); /* Collection of pairs where key is the key and value is line number */ + /* Read default config file and then overwrite it with a specific one from the directory */ int config_for_app(const char *application, /* Name of the application that will be used to get config for */ diff --git a/common/ini/ini_config_ut.c b/common/ini/ini_config_ut.c index 6fbade6..52e89cb 100644 --- a/common/ini/ini_config_ut.c +++ b/common/ini/ini_config_ut.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <errno.h> #include <unistd.h> +#include <fcntl.h> #define TRACE_HOME #include "ini_config.h" #include "collection.h" @@ -133,6 +134,78 @@ int single_file(void) return 0; } +int single_fd(void) +{ + int error; + struct collection_item *ini_config = NULL; + struct collection_item *error_set = NULL; + struct collection_item *lines = NULL; + + int fd = open("./ini.conf", O_RDONLY); + if (fd < 0) { + error = errno; + printf("Attempt to read configuration returned error: %d\n", error); + return error; + } + + error = config_from_fd("test", fd, "./ini.conf", &ini_config, + INI_STOP_ON_NONE, &error_set); + if (error) { + printf("Attempt to read configuration returned error: %d\n",error); + return error; + } + + col_debug_collection(ini_config, COL_TRAVERSE_DEFAULT); + col_print_collection(ini_config); + col_print_collection(error_set); + + printf("\n\n----------------------\n"); + /* Output parsing errors (if any) */ + print_file_parsing_errors(stdout, error_set); + printf("----------------------\n\n\n"); + + + free_ini_config(ini_config); + free_ini_config_errors(error_set); + close(fd); + + ini_config = NULL; + error_set = NULL; + + printf("TEST WITH LINES\n"); + + fd = open("./ini.conf", O_RDONLY); + if (fd < 0) { + error = errno; + printf("Attempt to read configuration returned error: %d\n", error); + return error; + } + error = config_from_fd_with_lines("test", fd, + "./ini.conf", + &ini_config, + INI_STOP_ON_NONE, + &error_set, &lines); + if (error) { + printf("Attempt to read configuration returned error: %d\n",error); + return error; + } + + col_debug_collection(ini_config, COL_TRAVERSE_DEFAULT); + col_debug_collection(lines, COL_TRAVERSE_DEFAULT); + + printf("\n\n----------------------\n"); + /* Output parsing errors (if any) */ + print_file_parsing_errors(stdout, error_set); + printf("----------------------\n\n\n"); + + + free_ini_config(ini_config); + free_ini_config_errors(error_set); + free_ini_config_lines(lines); + + return 0; +} + int negative_test(void) { int error; @@ -828,6 +901,7 @@ int main(int argc, char *argv[]) if ((error = basic_test()) || (error = single_file()) || + (error = single_fd()) || (error = real_test(NULL)) || /* This should result in merged configuration */ (error = real_test("./ini.conf")) || -- 1.5.5.6
_______________________________________________ sssd-devel mailing list sssd-devel@lists.fedorahosted.org https://fedorahosted.org/mailman/listinfo/sssd-devel