diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/system_views.sql | 6 | ||||
-rw-r--r-- | src/backend/libpq/hba.c | 31 | ||||
-rw-r--r-- | src/backend/utils/adt/hbafuncs.c | 136 |
3 files changed, 159 insertions, 14 deletions
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 9570a53e7be..9eaa51df290 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -617,6 +617,12 @@ CREATE VIEW pg_hba_file_rules AS REVOKE ALL ON pg_hba_file_rules FROM PUBLIC; REVOKE EXECUTE ON FUNCTION pg_hba_file_rules() FROM PUBLIC; +CREATE VIEW pg_ident_file_mappings AS + SELECT * FROM pg_ident_file_mappings() AS A; + +REVOKE ALL ON pg_ident_file_mappings FROM PUBLIC; +REVOKE EXECUTE ON FUNCTION pg_ident_file_mappings() FROM PUBLIC; + CREATE VIEW pg_timezone_abbrevs AS SELECT * FROM pg_timezone_abbrevs(); diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c index 673135144d4..f8393ca8ed4 100644 --- a/src/backend/libpq/hba.c +++ b/src/backend/libpq/hba.c @@ -887,25 +887,22 @@ do { \ } while (0) /* - * Macros for handling pg_ident problems. - * Much as above, but currently the message level is hardwired as LOG - * and there is no provision for an err_msg string. + * Macros for handling pg_ident problems, similar as above. * * IDENT_FIELD_ABSENT: - * Log a message and exit the function if the given ident field ListCell is - * not populated. + * Reports when the given ident field ListCell is not populated. * * IDENT_MULTI_VALUE: - * Log a message and exit the function if the given ident token List has more - * than one element. + * Reports when the given ident token List has more than one element. */ #define IDENT_FIELD_ABSENT(field) \ do { \ if (!field) { \ - ereport(LOG, \ + ereport(elevel, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ errmsg("missing entry in file \"%s\" at end of line %d", \ IdentFileName, line_num))); \ + *err_msg = psprintf("missing entry at end of line"); \ return NULL; \ } \ } while (0) @@ -913,11 +910,12 @@ do { \ #define IDENT_MULTI_VALUE(tokens) \ do { \ if (tokens->length > 1) { \ - ereport(LOG, \ + ereport(elevel, \ (errcode(ERRCODE_CONFIG_FILE_ERROR), \ errmsg("multiple values in ident field"), \ errcontext("line %d of configuration file \"%s\"", \ line_num, IdentFileName))); \ + *err_msg = psprintf("multiple values in ident field"); \ return NULL; \ } \ } while (0) @@ -2306,7 +2304,8 @@ load_hba(void) * Parse one tokenised line from the ident config file and store the result in * an IdentLine structure. * - * If parsing fails, log a message and return NULL. + * If parsing fails, log a message at ereport level elevel, store an error + * string in tok_line->err_msg and return NULL. * * If ident_user is a regular expression (ie. begins with a slash), it is * compiled and stored in IdentLine structure. @@ -2315,10 +2314,11 @@ load_hba(void) * to have set a memory context that will be reset if this function returns * NULL. */ -static IdentLine * -parse_ident_line(TokenizedAuthLine *tok_line) +IdentLine * +parse_ident_line(TokenizedAuthLine *tok_line, int elevel) { int line_num = tok_line->line_num; + char **err_msg = &tok_line->err_msg; ListCell *field; List *tokens; AuthToken *token; @@ -2372,11 +2372,14 @@ parse_ident_line(TokenizedAuthLine *tok_line) char errstr[100]; pg_regerror(r, &parsedline->re, errstr, sizeof(errstr)); - ereport(LOG, + ereport(elevel, (errcode(ERRCODE_INVALID_REGULAR_EXPRESSION), errmsg("invalid regular expression \"%s\": %s", parsedline->ident_user + 1, errstr))); + *err_msg = psprintf("invalid regular expression \"%s\": %s", + parsedline->ident_user + 1, errstr); + pfree(wstr); return NULL; } @@ -2627,7 +2630,7 @@ load_ident(void) continue; } - if ((newline = parse_ident_line(tok_line)) == NULL) + if ((newline = parse_ident_line(tok_line, LOG)) == NULL) { /* Parse error; remember there's trouble */ ok = false; diff --git a/src/backend/utils/adt/hbafuncs.c b/src/backend/utils/adt/hbafuncs.c index f46cd935a1c..9fe7b62c9a0 100644 --- a/src/backend/utils/adt/hbafuncs.c +++ b/src/backend/utils/adt/hbafuncs.c @@ -28,6 +28,9 @@ static ArrayType *get_hba_options(HbaLine *hba); static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, int lineno, HbaLine *hba, const char *err_msg); static void fill_hba_view(Tuplestorestate *tuple_store, TupleDesc tupdesc); +static void fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, + int lineno, IdentLine *ident, const char *err_msg); +static void fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc); /* @@ -426,3 +429,136 @@ pg_hba_file_rules(PG_FUNCTION_ARGS) PG_RETURN_NULL(); } + +/* Number of columns in pg_ident_file_mappings view */ +#define NUM_PG_IDENT_FILE_MAPPINGS_ATTS 5 + +/* + * fill_ident_line: build one row of pg_ident_file_mappings view, add it to + * tuplestore + * + * tuple_store: where to store data + * tupdesc: tuple descriptor for the view + * lineno: pg_ident.conf line number (must always be valid) + * ident: parsed line data (can be NULL, in which case err_msg should be set) + * err_msg: error message (NULL if none) + * + * Note: leaks memory, but we don't care since this is run in a short-lived + * memory context. + */ +static void +fill_ident_line(Tuplestorestate *tuple_store, TupleDesc tupdesc, + int lineno, IdentLine *ident, const char *err_msg) +{ + Datum values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS]; + bool nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS]; + HeapTuple tuple; + int index; + + Assert(tupdesc->natts == NUM_PG_IDENT_FILE_MAPPINGS_ATTS); + + memset(values, 0, sizeof(values)); + memset(nulls, 0, sizeof(nulls)); + index = 0; + + /* line_number */ + values[index++] = Int32GetDatum(lineno); + + if (ident != NULL) + { + values[index++] = CStringGetTextDatum(ident->usermap); + values[index++] = CStringGetTextDatum(ident->ident_user); + values[index++] = CStringGetTextDatum(ident->pg_role); + } + else + { + /* no parsing result, so set relevant fields to nulls */ + memset(&nulls[1], true, (NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 2) * sizeof(bool)); + } + + /* error */ + if (err_msg) + values[NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 1] = CStringGetTextDatum(err_msg); + else + nulls[NUM_PG_IDENT_FILE_MAPPINGS_ATTS - 1] = true; + + tuple = heap_form_tuple(tupdesc, values, nulls); + tuplestore_puttuple(tuple_store, tuple); +} + +/* + * Read the pg_ident.conf file and fill the tuplestore with view records. + */ +static void +fill_ident_view(Tuplestorestate *tuple_store, TupleDesc tupdesc) +{ + FILE *file; + List *ident_lines = NIL; + ListCell *line; + MemoryContext linecxt; + MemoryContext identcxt; + MemoryContext oldcxt; + + /* + * In the unlikely event that we can't open pg_ident.conf, we throw an + * error, rather than trying to report it via some sort of view entry. + * (Most other error conditions should result in a message in a view + * entry.) + */ + file = AllocateFile(IdentFileName, "r"); + if (file == NULL) + ereport(ERROR, + (errcode_for_file_access(), + errmsg("could not open usermap file \"%s\": %m", + IdentFileName))); + + linecxt = tokenize_auth_file(HbaFileName, file, &ident_lines, DEBUG3); + FreeFile(file); + + /* Now parse all the lines */ + identcxt = AllocSetContextCreate(CurrentMemoryContext, + "ident parser context", + ALLOCSET_SMALL_SIZES); + oldcxt = MemoryContextSwitchTo(identcxt); + foreach(line, ident_lines) + { + TokenizedAuthLine *tok_line = (TokenizedAuthLine *) lfirst(line); + IdentLine *identline = NULL; + + /* don't parse lines that already have errors */ + if (tok_line->err_msg == NULL) + identline = parse_ident_line(tok_line, DEBUG3); + + fill_ident_line(tuple_store, tupdesc, tok_line->line_num, identline, + tok_line->err_msg); + } + + /* Free tokenizer memory */ + MemoryContextDelete(linecxt); + /* Free parse_ident_line memory */ + MemoryContextSwitchTo(oldcxt); + MemoryContextDelete(identcxt); +} + +/* + * SQL-accessible SRF to return all the entries in the pg_ident.conf file. + */ +Datum +pg_ident_file_mappings(PG_FUNCTION_ARGS) +{ + ReturnSetInfo *rsi; + + /* + * Build tuplestore to hold the result rows. We must use the Materialize + * mode to be safe against HBA file changes while the cursor is open. It's + * also more efficient than having to look up our current position in the + * parsed list every time. + */ + SetSingleFuncCall(fcinfo, 0); + + /* Fill the tuplestore */ + rsi = (ReturnSetInfo *) fcinfo->resultinfo; + fill_ident_view(rsi->setResult, rsi->setDesc); + + PG_RETURN_NULL(); +} |