dpkg (1.18.25) stretch; urgency=medium
[dpkg] / lib / dpkg / trigdeferred.c
CommitLineData
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
42static struct varbuf fn, newfn;
43
44static const struct trigdefmeths *trigdef;
45
46/*---------- Deferred file handling ----------*/
47
48static char *triggersdir;
49static int lock_fd = -1;
50static FILE *old_deferred;
51static FILE *trig_new_deferred;
52
53static void
54constructfn(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 */
74enum trigdef_update_status
75trigdef_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
145void
146trigdef_set_methods(const struct trigdefmeths *methods)
147{
148 trigdef = methods;
149}
150
151void
152trigdef_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
161static void
162trigdef_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. */
170static char *
171trigdef_skip_whitespace(char *ptr)
172{
173 while (*ptr) {
174 if (!c_iswhite(*ptr))
175 break;
176 ptr++;
177 }
178
179 return ptr;
180}
181
182int
183trigdef_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
248void
249trigdef_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}