Home | History | Annotate | Line # | Download | only in util
      1 /*	$NetBSD: dict_pipe.c,v 1.3 2026/05/09 18:49:22 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	dict_pipe 3
      6 /* SUMMARY
      7 /*	dictionary manager interface for pipelined tables
      8 /* SYNOPSIS
      9 /*	#include <dict_pipe.h>
     10 /*
     11 /*	DICT	*dict_pipe_open(name, open_flags, dict_flags)
     12 /*	const char *name;
     13 /*	int	open_flags;
     14 /*	int	dict_flags;
     15 /* DESCRIPTION
     16 /*	dict_pipe_open() opens a pipeline of one or more tables.
     17 /*	Example: "\fBpipemap:{\fItype_1:name_1, ..., type_n:name_n\fR}".
     18 /*
     19 /*	Each "pipemap:" query is given to the first table.  Each
     20 /*	lookup result becomes the query for the next table in the
     21 /*	pipeline, and the last table produces the final result.
     22 /*	When any table lookup produces no result, the pipeline
     23 /*	produces no result.
     24 /*
     25 /*	The first and last characters of the "pipemap:" table name
     26 /*	must be '{' and '}'. Within these, individual maps are
     27 /*	separated with comma or whitespace.
     28 /*
     29 /*	The open_flags and dict_flags arguments are passed on to
     30 /*	the underlying dictionaries.
     31 /* SEE ALSO
     32 /*	dict(3) generic dictionary manager
     33 /* LICENSE
     34 /* .ad
     35 /* .fi
     36 /*	The Secure Mailer license must be distributed with this software.
     37 /* AUTHOR(S)
     38 /*	Wietse Venema
     39 /*	IBM T.J. Watson Research
     40 /*	P.O. Box 704
     41 /*	Yorktown Heights, NY 10598, USA
     42 /*--*/
     43 
     44 /* System library. */
     45 
     46 #include <sys_defs.h>
     47 #include <string.h>
     48 
     49 /* Utility library. */
     50 
     51 #include <msg.h>
     52 #include "mymalloc.h"
     53 #include "htable.h"
     54 #include "dict.h"
     55 #include "dict_pipe.h"
     56 #include "stringops.h"
     57 #include "vstring.h"
     58 
     59 /* Application-specific. */
     60 
     61 typedef struct {
     62     DICT    dict;			/* generic members */
     63     ARGV   *map_pipe;			/* pipelined tables */
     64     VSTRING *qr_buf;			/* query/reply buffer */
     65 } DICT_PIPE;
     66 
     67 #define STR(x) vstring_str(x)
     68 
     69 /* dict_pipe_lookup - search pipelined tables */
     70 
     71 static const char *dict_pipe_lookup(DICT *dict, const char *query)
     72 {
     73     static const char myname[] = "dict_pipe_lookup";
     74     DICT_PIPE *dict_pipe = (DICT_PIPE *) dict;
     75     DICT   *map;
     76     char  **cpp;
     77     char   *reg_name;
     78     const char *result = 0;
     79 
     80     vstring_strcpy(dict_pipe->qr_buf, query);
     81     for (cpp = dict_pipe->map_pipe->argv; (reg_name = *cpp) != 0; cpp++) {
     82 	if ((map = dict_handle(reg_name)) == 0)
     83 	    msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
     84 	if ((result = dict_get(map, STR(dict_pipe->qr_buf))) == 0)
     85 	    DICT_ERR_VAL_RETURN(dict, map->error, result);
     86 	vstring_strcpy(dict_pipe->qr_buf, result);
     87     }
     88     DICT_ERR_VAL_RETURN(dict, DICT_ERR_NONE, STR(dict_pipe->qr_buf));
     89 }
     90 
     91 /* dict_pipe_close - disassociate from pipelined tables */
     92 
     93 static void dict_pipe_close(DICT *dict)
     94 {
     95     static const char myname[] = "dict_pipe_close";
     96     DICT_PIPE *dict_pipe = (DICT_PIPE *) dict;
     97     DICT   *map;
     98     char  **cpp;
     99     char   *reg_name;
    100 
    101     for (cpp = dict_pipe->map_pipe->argv; (reg_name = *cpp) != 0; cpp++) {
    102 	if ((map = dict_handle(reg_name)) == 0)
    103 	    msg_panic("%s: dictionary \"%s\" not found", myname, reg_name);
    104 	dict_close(map);
    105     }
    106     argv_free(dict_pipe->map_pipe);
    107     vstring_free(dict_pipe->qr_buf);
    108     dict_free(dict);
    109 }
    110 
    111 /* dict_pipe_open - open pipelined tables */
    112 
    113 DICT   *dict_pipe_open(const char *name, int open_flags, int dict_flags)
    114 {
    115     static const char myname[] = "dict_pipe_open";
    116     DICT_PIPE *dict_pipe;
    117     char   *saved_name = 0;
    118     char   *dict_type_name;
    119     ARGV   *argv = 0;
    120     char  **cpp;
    121     DICT   *dict;
    122     int     match_flags = 0;
    123     struct DICT_OWNER aggr_owner;
    124     size_t  len;
    125 
    126     /*
    127      * Clarity first. Let the optimizer worry about redundant code.
    128      */
    129 #define DICT_PIPE_RETURN(x) do { \
    130 	    if (saved_name != 0) \
    131 		myfree(saved_name); \
    132 	    if (argv != 0) \
    133 		argv_free(argv); \
    134 	    return (x); \
    135 	} while (0)
    136 
    137     /*
    138      * Sanity checks.
    139      */
    140     if (open_flags != O_RDONLY)
    141 	DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
    142 					open_flags, dict_flags,
    143 				  "%s:%s map requires O_RDONLY access mode",
    144 					DICT_TYPE_PIPE, name));
    145 
    146     /*
    147      * Split the table name into its constituent parts.
    148      */
    149     if ((len = balpar(name, CHARS_BRACE)) == 0 || name[len] != 0
    150 	|| *(saved_name = mystrndup(name + 1, len - 2)) == 0
    151 	|| ((argv = argv_splitq(saved_name, CHARS_COMMA_SP, CHARS_BRACE)),
    152 	    (argv->argc == 0)))
    153 	DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
    154 					open_flags, dict_flags,
    155 					"bad syntax: \"%s:%s\"; "
    156 					"need \"%s:{type:name...}\"",
    157 					DICT_TYPE_PIPE, name,
    158 					DICT_TYPE_PIPE));
    159 
    160     /*
    161      * Check all underlying table specs before registering any of them to
    162      * avoid leaking refcounts if one of them is bad.
    163      */
    164     for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
    165 	if (strchr(dict_type_name, ':') == 0)
    166 	    DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
    167 					    open_flags, dict_flags,
    168 					    "bad syntax: \"%s:%s\"; "
    169 					    "need \"%s:{type:name...}\"",
    170 					    DICT_TYPE_PIPE, name,
    171 					    DICT_TYPE_PIPE));
    172     }
    173 
    174     /*
    175      * The least-trusted table in the pipeline determines the over-all trust
    176      * level. The first table determines the pattern-matching flags.
    177      */
    178     DICT_OWNER_AGGREGATE_INIT(aggr_owner);
    179     for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
    180 	if (msg_verbose)
    181 	    msg_info("%s: %s", myname, dict_type_name);
    182 	dict = dict_open(dict_type_name, open_flags, dict_flags);
    183 	argv_replace_one(argv, cpp - argv->argv, dict->reg_name);
    184 	DICT_OWNER_AGGREGATE_UPDATE(aggr_owner, dict->owner);
    185 	if (cpp == argv->argv)
    186 	    match_flags = dict->flags & (DICT_FLAG_FIXED | DICT_FLAG_PATTERN);
    187     }
    188 
    189     /*
    190      * Bundle up the result.
    191      */
    192     dict_pipe =
    193 	(DICT_PIPE *) dict_alloc(DICT_TYPE_PIPE, name, sizeof(*dict_pipe));
    194     dict_pipe->dict.lookup = dict_pipe_lookup;
    195     dict_pipe->dict.close = dict_pipe_close;
    196     dict_pipe->dict.flags = dict_flags | match_flags;
    197     dict_pipe->dict.owner = aggr_owner;
    198     dict_pipe->qr_buf = vstring_alloc(100);
    199     dict_pipe->map_pipe = argv;
    200     argv = 0;
    201     DICT_PIPE_RETURN(&dict_pipe->dict);
    202 }
    203