Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * libdpkg - Debian packaging suite library routines | |
3 | * trigdeferred.c - parsing of triggers/Deferred | |
4 | * | |
5 | * Copyright © 2007 Canonical Ltd | |
6 | * written by Ian Jackson <ijackson@chiark.greenend.org.uk> | |
7 | * Copyright © 2008-2016 Guillem Jover <guillem@debian.org> | |
8 | * | |
9 | * This is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation; either version 2 of the License, or | |
12 | * (at your option) any later version. | |
13 | * | |
14 | * This is distributed in the hope that it will be useful, | |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | * GNU General Public License for more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
21 | */ | |
22 | ||
23 | #include <config.h> | |
24 | #include <compat.h> | |
25 | ||
26 | #include <sys/stat.h> | |
27 | ||
28 | #include <errno.h> | |
29 | #include <fcntl.h> | |
30 | #include <limits.h> | |
31 | #include <stdlib.h> | |
32 | ||
33 | #include <dpkg/i18n.h> | |
34 | #include <dpkg/dpkg.h> | |
35 | #include <dpkg/dpkg-db.h> | |
36 | #include <dpkg/c-ctype.h> | |
37 | #include <dpkg/file.h> | |
38 | #include <dpkg/dir.h> | |
39 | #include <dpkg/trigdeferred.h> | |
40 | #include <dpkg/triglib.h> | |
41 | ||
42 | static struct varbuf fn, newfn; | |
43 | ||
44 | static const struct trigdefmeths *trigdef; | |
45 | ||
46 | /*---------- Deferred file handling ----------*/ | |
47 | ||
48 | static char *triggersdir; | |
49 | static int lock_fd = -1; | |
50 | static FILE *old_deferred; | |
51 | static FILE *trig_new_deferred; | |
52 | ||
53 | static void | |
54 | constructfn(struct varbuf *vb, const char *dir, const char *tail) | |
55 | { | |
56 | varbuf_reset(vb); | |
57 | varbuf_add_str(vb, dir); | |
58 | varbuf_add_char(vb, '/'); | |
59 | varbuf_add_str(vb, tail); | |
60 | varbuf_end_str(vb); | |
61 | } | |
62 | ||
63 | /** | |
64 | * Start processing of the triggers deferred file. | |
65 | * | |
66 | * @retval -1 Lock ENOENT with O_CREAT (directory does not exist). | |
67 | * @retval -2 Unincorp empty, TDUF_WRITE_IF_EMPTY unset. | |
68 | * @retval -3 Unincorp ENOENT, TDUF_WRITE_IF_ENOENT unset. | |
69 | * @retval 1 Unincorp ENOENT, TDUF_WRITE_IF_ENOENT set. | |
70 | * @retval 2 Ok. | |
71 | * | |
72 | * For positive return values the caller must call trigdef_update_done! | |
73 | */ | |
74 | enum trigdef_update_status | |
75 | trigdef_update_start(enum trigdef_update_flags uf) | |
76 | { | |
77 | triggersdir = dpkg_db_get_path(TRIGGERSDIR); | |
78 | ||
79 | if (uf & TDUF_WRITE) { | |
80 | constructfn(&fn, triggersdir, TRIGGERSLOCKFILE); | |
81 | if (lock_fd == -1) { | |
82 | lock_fd = open(fn.buf, O_RDWR | O_CREAT | O_TRUNC, 0600); | |
83 | if (lock_fd == -1) { | |
84 | if (!(errno == ENOENT && (uf & TDUF_NO_LOCK_OK))) | |
85 | ohshite(_("unable to open/create " | |
86 | "triggers lockfile '%.250s'"), | |
87 | fn.buf); | |
88 | return TDUS_ERROR_NO_DIR; | |
89 | } | |
90 | } | |
91 | ||
92 | file_lock(&lock_fd, FILE_LOCK_WAIT, fn.buf, _("triggers area")); | |
93 | } | |
94 | ||
95 | constructfn(&fn, triggersdir, TRIGGERSDEFERREDFILE); | |
96 | ||
97 | if (old_deferred) | |
98 | fclose(old_deferred); | |
99 | old_deferred = fopen(fn.buf, "r"); | |
100 | if (!old_deferred) { | |
101 | if (errno != ENOENT) | |
102 | ohshite(_("unable to open triggers deferred file '%.250s'"), | |
103 | fn.buf); | |
104 | if (!(uf & TDUF_WRITE_IF_ENOENT)) { | |
105 | if (uf & TDUF_WRITE) | |
106 | pop_cleanup(ehflag_normaltidy); | |
107 | return TDUS_ERROR_NO_DEFERRED; | |
108 | } | |
109 | } else { | |
110 | struct stat stab; | |
111 | int rc; | |
112 | ||
113 | setcloexec(fileno(old_deferred), fn.buf); | |
114 | ||
115 | rc = fstat(fileno(old_deferred), &stab); | |
116 | if (rc < 0) | |
117 | ohshite(_("unable to stat triggers deferred file '%.250s'"), | |
118 | fn.buf); | |
119 | ||
120 | if (stab.st_size == 0 && !(uf & TDUF_WRITE_IF_EMPTY)) { | |
121 | if (uf & TDUF_WRITE) | |
122 | pop_cleanup(ehflag_normaltidy); | |
123 | return TDUS_ERROR_EMPTY_DEFERRED; | |
124 | } | |
125 | } | |
126 | ||
127 | if (uf & TDUF_WRITE) { | |
128 | constructfn(&newfn, triggersdir, TRIGGERSDEFERREDFILE ".new"); | |
129 | if (trig_new_deferred) | |
130 | fclose(trig_new_deferred); | |
131 | trig_new_deferred = fopen(newfn.buf, "w"); | |
132 | if (!trig_new_deferred) | |
133 | ohshite(_("unable to open/create new triggers deferred file '%.250s'"), | |
134 | newfn.buf); | |
135 | ||
136 | setcloexec(fileno(trig_new_deferred), newfn.buf); | |
137 | } | |
138 | ||
139 | if (!old_deferred) | |
140 | return TDUS_NO_DEFERRED; | |
141 | ||
142 | return TDUS_OK; | |
143 | } | |
144 | ||
145 | void | |
146 | trigdef_set_methods(const struct trigdefmeths *methods) | |
147 | { | |
148 | trigdef = methods; | |
149 | } | |
150 | ||
151 | void | |
152 | trigdef_update_printf(const char *format, ...) | |
153 | { | |
154 | va_list ap; | |
155 | ||
156 | va_start(ap, format); | |
157 | vfprintf(trig_new_deferred, format, ap); | |
158 | va_end(ap); | |
159 | } | |
160 | ||
161 | static void | |
162 | trigdef_parse_error(int line_num, const char *line, const char *ptr) | |
163 | { | |
164 | ohshit(_("syntax error in triggers deferred file '%.250s' at " | |
165 | "line %d character %zd '%s'"), | |
166 | fn.buf, line_num, ptr - line + 1, ptr); | |
167 | } | |
168 | ||
169 | /* Trim leading space. */ | |
170 | static char * | |
171 | trigdef_skip_whitespace(char *ptr) | |
172 | { | |
173 | while (*ptr) { | |
174 | if (!c_iswhite(*ptr)) | |
175 | break; | |
176 | ptr++; | |
177 | } | |
178 | ||
179 | return ptr; | |
180 | } | |
181 | ||
182 | int | |
183 | trigdef_parse(void) | |
184 | { | |
185 | char line[_POSIX2_LINE_MAX]; | |
186 | char *ptr, *ptr_ini; | |
187 | int line_num = 0; | |
188 | ||
189 | while (fgets_checked(line, sizeof(line), old_deferred, fn.buf) > 0) { | |
190 | line_num++; | |
191 | ||
192 | ptr = trigdef_skip_whitespace(line); | |
193 | ||
194 | /* Skip comments and empty lines. */ | |
195 | if (*ptr == '\0' || *ptr == '#') | |
196 | continue; | |
197 | ||
198 | /* Parse the trigger directive. */ | |
199 | ptr_ini = ptr; | |
200 | while (*ptr) { | |
201 | if (*ptr < 0x21 || *ptr > 0x7e) | |
202 | break; | |
203 | ptr++; | |
204 | } | |
205 | ||
206 | if (*ptr == '\0' || ptr_ini == ptr) | |
207 | trigdef_parse_error(line_num, line, ptr); | |
208 | *ptr++ = '\0'; | |
209 | ||
210 | /* Set the trigger directive. */ | |
211 | trigdef->trig_begin(ptr_ini); | |
212 | ||
213 | /* Parse the package names. */ | |
214 | while (*ptr) { | |
215 | ptr = trigdef_skip_whitespace(ptr); | |
216 | ||
217 | ptr_ini = ptr; | |
218 | if (*ptr == '\0' || | |
219 | !(c_isdigit(*ptr) || c_islower(*ptr) || *ptr == '-')) | |
220 | trigdef_parse_error(line_num, line, ptr); | |
221 | ||
222 | while (*++ptr) { | |
223 | if (!c_isdigit(*ptr) && !c_islower(*ptr) && | |
224 | *ptr != '-' && *ptr != ':' && | |
225 | *ptr != '+' && *ptr != '.') | |
226 | break; | |
227 | } | |
228 | ||
229 | if (*ptr != '\0' && *ptr != '#' && !c_iswhite(*ptr)) | |
230 | trigdef_parse_error(line_num, line, ptr); | |
231 | *ptr++ = '\0'; | |
232 | ||
233 | if (ptr_ini[0] == '-' && ptr_ini[1]) | |
234 | ohshit(_("invalid package name '%.250s' in " | |
235 | "triggers deferred file '%.250s'"), | |
236 | ptr_ini, fn.buf); | |
237 | ||
238 | /* Set the package name. */ | |
239 | trigdef->package(ptr_ini); | |
240 | } | |
241 | ||
242 | trigdef->trig_end(); | |
243 | } | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
248 | void | |
249 | trigdef_process_done(void) | |
250 | { | |
251 | int r; | |
252 | ||
253 | if (old_deferred) { | |
254 | if (ferror(old_deferred)) | |
255 | ohshite(_("error reading triggers deferred file '%.250s'"), | |
256 | fn.buf); | |
257 | fclose(old_deferred); | |
258 | old_deferred = NULL; | |
259 | } | |
260 | ||
261 | if (trig_new_deferred) { | |
262 | if (ferror(trig_new_deferred)) | |
263 | ohshite(_("unable to write new triggers deferred " | |
264 | "file '%.250s'"), newfn.buf); | |
265 | r = fclose(trig_new_deferred); | |
266 | trig_new_deferred = NULL; | |
267 | if (r) | |
268 | ohshite(_("unable to close new triggers deferred " | |
269 | "file '%.250s'"), newfn.buf); | |
270 | ||
271 | if (rename(newfn.buf, fn.buf)) | |
272 | ohshite(_("unable to install new triggers deferred " | |
273 | "file '%.250s'"), fn.buf); | |
274 | ||
275 | dir_sync_path(triggersdir); | |
276 | } | |
277 | ||
278 | free(triggersdir); | |
279 | triggersdir = NULL; | |
280 | ||
281 | /* Unlock. */ | |
282 | if (lock_fd >= 0) | |
283 | pop_cleanup(ehflag_normaltidy); | |
284 | } |