1 /*        $NetBSD: mkmap_open.c,v 1.2 2023/12/23 20:30:46 christos Exp $        */
2 
3 /*++
4 /* NAME
5 /*        mkmap_open 3
6 /* SUMMARY
7 /*        create or rewrite database, generic interface
8 /* SYNOPSIS
9 /*        #include <mkmap.h>
10 /*
11 /*        typedef struct MKMAP {
12 /*            struct DICT *(*open) (const char *, int, int); /* dict_xx_open() */
13 /*            struct DICT *dict;                                      /* dict_xx_open() result */
14 /*            void    (*after_open) (struct MKMAP *);       /* may be null */
15 /*            void    (*after_close) (struct MKMAP *);      /* may be null */
16 /*            int     multi_writer;                         /* multi-writer safe */
17 /*        } MKMAP;
18 /*
19 /*        MKMAP     *mkmap_open(type, path, open_flags, dict_flags)
20 /*        char      *type;
21 /*        char      *path;
22 /*        int       open_flags;
23 /*        int       dict_flags;
24 /*
25 /*        void      mkmap_append(mkmap, key, value, lineno)
26 /*        MKMAP     *mkmap;
27 /*        char      *key;
28 /*        char      *value;
29 /*        int       lineno;
30 /*
31 /*        void      mkmap_close(mkmap)
32 /*        MKMAP     *mkmap;
33 /* DESCRIPTION
34 /*        This module implements support for creating Postfix databases.
35 /*        It is a dict(3) wrapper that adds global locking to dict-level
36 /*        routines where appropriate.
37 /*
38 /*        mkmap_open() creates or truncates the named database, after
39 /*        appending the appropriate suffixes to the specified filename.
40 /*        Before the database is updated, it is locked for exclusive
41 /*        access, and signal delivery is suspended.
42 /*        See dict(3) for a description of \fBopen_flags\fR and
43 /*        \fBdict_flags\fR.  All errors are fatal.
44 /*
45 /*        mkmap_append() appends the named (key, value) pair to the
46 /*        database. Update errors are fatal; duplicate keys are ignored
47 /*        (but a warning is issued).
48 /*        \fBlineno\fR is used for diagnostics.
49 /*
50 /*        mkmap_close() closes the database, releases any locks,
51 /*        and resumes signal delivery. All errors are fatal.
52 /* SEE ALSO
53 /*        sigdelay(3) suspend/resume signal delivery
54 /* LICENSE
55 /* .ad
56 /* .fi
57 /*        The Secure Mailer license must be distributed with this software.
58 /* AUTHOR(S)
59 /*        Wietse Venema
60 /*        IBM T.J. Watson Research
61 /*        P.O. Box 704
62 /*        Yorktown Heights, NY 10598, USA
63 /*
64 /*        Wietse Venema
65 /*        Google, Inc.
66 /*        111 8th Avenue
67 /*        New York, NY 10011, USA
68 /*--*/
69 
70 /* System library. */
71 
72 #include <sys_defs.h>
73 #include <unistd.h>
74 #include <string.h>
75 
76 /* Utility library. */
77 
78 #include <msg.h>
79 #include <dict.h>
80 #include <sigdelay.h>
81 #include <mymalloc.h>
82 #include <stringops.h>
83 #include <mkmap.h>
84 
85 /* mkmap_close - close database */
86 
mkmap_close(MKMAP * mkmap)87 void    mkmap_close(MKMAP *mkmap)
88 {
89 
90     /*
91      * Close the database.
92      */
93     dict_close(mkmap->dict);
94 
95     /*
96      * Do whatever special processing is needed after closing the database,
97      * such as releasing a global exclusive lock on the database file.
98      * Individual Postfix dict modules implement locking only for individual
99      * record operations, because most Postfix applications don't need global
100      * exclusive locks.
101      */
102     if (mkmap->after_close)
103           mkmap->after_close(mkmap);
104 
105     /*
106      * Resume signal delivery.
107      */
108     if (mkmap->multi_writer == 0)
109           sigresume();
110 
111     /*
112      * Cleanup.
113      */
114     myfree((void *) mkmap);
115 }
116 
117 /* mkmap_open - create or truncate database */
118 
mkmap_open(const char * type,const char * path,int open_flags,int dict_flags)119 MKMAP  *mkmap_open(const char *type, const char *path,
120                                int open_flags, int dict_flags)
121 {
122     MKMAP  *mkmap;
123     const DICT_OPEN_INFO *dp;
124 
125     /*
126      * Find out what map type to use.
127      */
128     if ((dp = dict_open_lookup(type)) == 0)
129           msg_fatal("unsupported map type: %s", type);
130     if (dp->mkmap_fn == 0)
131           msg_fatal("no 'map create' support for this type: %s", type);
132     if (msg_verbose)
133           msg_info("open %s %s", type, path);
134 
135     /*
136      * Do whatever before-open initialization is needed, such as acquiring a
137      * global exclusive lock on an existing database file. Individual Postfix
138      * dict modules implement locking only for individual record operations,
139      * because most Postfix applications don't need global exclusive locks.
140      */
141     mkmap = dp->mkmap_fn(path);
142 
143     /*
144      * Delay signal delivery, so that we won't leave the database in an
145      * inconsistent state if we can avoid it.
146      */
147     sigdelay();
148 
149     /*
150      * Truncate the database upon open, and update it. Read-write mode is
151      * needed because the underlying routines read as well as write. We
152      * explicitly clobber lock_fd to trigger a fatal error when a map wants
153      * to unlock the database after individual transactions: that would
154      * result in race condition problems. We clobbber stat_fd as well,
155      * because that, too, is used only for individual-transaction clients.
156      */
157     mkmap->dict = mkmap->open(path, open_flags, dict_flags);
158     mkmap->dict->lock_fd = -1;                              /* XXX just in case */
159     mkmap->dict->stat_fd = -1;                              /* XXX just in case */
160     mkmap->dict->flags |= DICT_FLAG_DUP_WARN;
161     mkmap->multi_writer = (mkmap->dict->flags & DICT_FLAG_MULTI_WRITER);
162 
163     /*
164      * Do whatever post-open initialization is needed, such as acquiring a
165      * global exclusive lock on a database file that did not exist.
166      * Individual Postfix dict modules implement locking only for individual
167      * record operations, because most Postfix applications don't need global
168      * exclusive locks.
169      */
170     if (mkmap->after_open)
171           mkmap->after_open(mkmap);
172 
173     /*
174      * Wrap the dictionary for UTF-8 syntax checks and casefolding.
175      */
176     if ((mkmap->dict->flags & DICT_FLAG_UTF8_ACTIVE) == 0
177           && DICT_NEED_UTF8_ACTIVATION(util_utf8_enable, dict_flags))
178           mkmap->dict = dict_utf8_activate(mkmap->dict);
179 
180     /*
181      * Resume signal delivery if multi-writer safe.
182      */
183     if (mkmap->multi_writer)
184           sigresume();
185 
186     return (mkmap);
187 }
188