Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * libdpkg - Debian packaging suite library routines | |
3 | * dump.c - code to write in-core database to a file | |
4 | * | |
5 | * Copyright © 1995 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2001 Wichert Akkerman | |
7 | * Copyright © 2006,2008-2014 Guillem Jover <guillem@debian.org> | |
8 | * Copyright © 2011 Linaro Limited | |
9 | * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> | |
10 | * | |
11 | * This is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License as published by | |
13 | * the Free Software Foundation; either version 2 of the License, or | |
14 | * (at your option) any later version. | |
15 | * | |
16 | * This is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
23 | */ | |
24 | ||
25 | /* FIXME: Don't write uninteresting packages. */ | |
26 | ||
27 | #include <config.h> | |
28 | #include <compat.h> | |
29 | ||
30 | #include <sys/types.h> | |
31 | #include <sys/stat.h> | |
32 | ||
33 | #include <assert.h> | |
34 | #include <errno.h> | |
35 | #include <string.h> | |
36 | #include <unistd.h> | |
37 | #include <stdio.h> | |
38 | #include <stdlib.h> | |
39 | #include <stdbool.h> | |
40 | ||
41 | #include <dpkg/i18n.h> | |
42 | #include <dpkg/dpkg.h> | |
43 | #include <dpkg/dpkg-db.h> | |
44 | #include <dpkg/string.h> | |
45 | #include <dpkg/dir.h> | |
46 | #include <dpkg/parsedump.h> | |
47 | ||
48 | static inline void | |
49 | varbuf_add_fieldname(struct varbuf *vb, const struct fieldinfo *fip) | |
50 | { | |
51 | varbuf_add_str(vb, fip->name); | |
52 | varbuf_add_str(vb, ": "); | |
53 | } | |
54 | ||
55 | void | |
56 | w_name(struct varbuf *vb, | |
57 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
58 | enum fwriteflags flags, const struct fieldinfo *fip) | |
59 | { | |
60 | assert(pkg->set->name); | |
61 | if (flags&fw_printheader) | |
62 | varbuf_add_str(vb, "Package: "); | |
63 | varbuf_add_str(vb, pkg->set->name); | |
64 | if (flags&fw_printheader) | |
65 | varbuf_add_char(vb, '\n'); | |
66 | } | |
67 | ||
68 | void | |
69 | w_version(struct varbuf *vb, | |
70 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
71 | enum fwriteflags flags, const struct fieldinfo *fip) | |
72 | { | |
73 | if (!dpkg_version_is_informative(&pkgbin->version)) | |
74 | return; | |
75 | if (flags&fw_printheader) | |
76 | varbuf_add_str(vb, "Version: "); | |
77 | varbufversion(vb, &pkgbin->version, vdew_nonambig); | |
78 | if (flags&fw_printheader) | |
79 | varbuf_add_char(vb, '\n'); | |
80 | } | |
81 | ||
82 | void | |
83 | w_configversion(struct varbuf *vb, | |
84 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
85 | enum fwriteflags flags, const struct fieldinfo *fip) | |
86 | { | |
87 | if (pkgbin != &pkg->installed) | |
88 | return; | |
89 | if (!dpkg_version_is_informative(&pkg->configversion)) | |
90 | return; | |
91 | if (pkg->status == PKG_STAT_INSTALLED || | |
92 | pkg->status == PKG_STAT_NOTINSTALLED || | |
93 | pkg->status == PKG_STAT_TRIGGERSPENDING) | |
94 | return; | |
95 | if (flags&fw_printheader) | |
96 | varbuf_add_str(vb, "Config-Version: "); | |
97 | varbufversion(vb, &pkg->configversion, vdew_nonambig); | |
98 | if (flags&fw_printheader) | |
99 | varbuf_add_char(vb, '\n'); | |
100 | } | |
101 | ||
102 | void | |
103 | w_null(struct varbuf *vb, | |
104 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
105 | enum fwriteflags flags, const struct fieldinfo *fip) | |
106 | { | |
107 | } | |
108 | ||
109 | void | |
110 | w_section(struct varbuf *vb, | |
111 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
112 | enum fwriteflags flags, const struct fieldinfo *fip) | |
113 | { | |
114 | const char *value = pkg->section; | |
115 | ||
116 | if (str_is_unset(value)) | |
117 | return; | |
118 | if (flags&fw_printheader) | |
119 | varbuf_add_str(vb, "Section: "); | |
120 | varbuf_add_str(vb, value); | |
121 | if (flags&fw_printheader) | |
122 | varbuf_add_char(vb, '\n'); | |
123 | } | |
124 | ||
125 | void | |
126 | w_charfield(struct varbuf *vb, | |
127 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
128 | enum fwriteflags flags, const struct fieldinfo *fip) | |
129 | { | |
130 | const char *value = STRUCTFIELD(pkgbin, fip->integer, const char *); | |
131 | ||
132 | if (str_is_unset(value)) | |
133 | return; | |
134 | if (flags & fw_printheader) | |
135 | varbuf_add_fieldname(vb, fip); | |
136 | varbuf_add_str(vb, value); | |
137 | if (flags&fw_printheader) | |
138 | varbuf_add_char(vb, '\n'); | |
139 | } | |
140 | ||
141 | void | |
142 | w_filecharf(struct varbuf *vb, | |
143 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
144 | enum fwriteflags flags, const struct fieldinfo *fip) | |
145 | { | |
146 | struct filedetails *fdp; | |
147 | ||
148 | if (pkgbin != &pkg->available) | |
149 | return; | |
150 | fdp = pkg->files; | |
151 | if (!fdp || !STRUCTFIELD(fdp, fip->integer, const char *)) | |
152 | return; | |
153 | ||
154 | if (flags&fw_printheader) { | |
155 | varbuf_add_str(vb, fip->name); | |
156 | varbuf_add_char(vb, ':'); | |
157 | } | |
158 | ||
159 | while (fdp) { | |
160 | varbuf_add_char(vb, ' '); | |
161 | varbuf_add_str(vb, STRUCTFIELD(fdp, fip->integer, const char *)); | |
162 | fdp= fdp->next; | |
163 | } | |
164 | ||
165 | if (flags&fw_printheader) | |
166 | varbuf_add_char(vb, '\n'); | |
167 | } | |
168 | ||
169 | void | |
170 | w_booleandefno(struct varbuf *vb, | |
171 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
172 | enum fwriteflags flags, const struct fieldinfo *fip) | |
173 | { | |
174 | bool value = STRUCTFIELD(pkgbin, fip->integer, bool); | |
175 | ||
176 | if ((flags & fw_printheader) && !value) | |
177 | return; | |
178 | ||
179 | if (flags & fw_printheader) | |
180 | varbuf_add_fieldname(vb, fip); | |
181 | ||
182 | varbuf_add_str(vb, value ? "yes" : "no"); | |
183 | ||
184 | if (flags & fw_printheader) | |
185 | varbuf_add_char(vb, '\n'); | |
186 | } | |
187 | ||
188 | void | |
189 | w_multiarch(struct varbuf *vb, | |
190 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
191 | enum fwriteflags flags, const struct fieldinfo *fip) | |
192 | { | |
193 | int value = STRUCTFIELD(pkgbin, fip->integer, int); | |
194 | ||
195 | if ((flags & fw_printheader) && !value) | |
196 | return; | |
197 | ||
198 | if (flags & fw_printheader) | |
199 | varbuf_add_fieldname(vb, fip); | |
200 | ||
201 | varbuf_add_str(vb, multiarchinfos[value].name); | |
202 | ||
203 | if (flags & fw_printheader) | |
204 | varbuf_add_char(vb, '\n'); | |
205 | } | |
206 | ||
207 | void | |
208 | w_architecture(struct varbuf *vb, | |
209 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
210 | enum fwriteflags flags, const struct fieldinfo *fip) | |
211 | { | |
212 | if (!pkgbin->arch) | |
213 | return; | |
214 | if (pkgbin->arch->type == DPKG_ARCH_NONE) | |
215 | return; | |
216 | if (pkgbin->arch->type == DPKG_ARCH_EMPTY) | |
217 | return; | |
218 | ||
219 | if (flags & fw_printheader) | |
220 | varbuf_add_fieldname(vb, fip); | |
221 | varbuf_add_str(vb, pkgbin->arch->name); | |
222 | if (flags & fw_printheader) | |
223 | varbuf_add_char(vb, '\n'); | |
224 | } | |
225 | ||
226 | void | |
227 | w_priority(struct varbuf *vb, | |
228 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
229 | enum fwriteflags flags, const struct fieldinfo *fip) | |
230 | { | |
231 | if (pkg->priority == PKG_PRIO_UNKNOWN) | |
232 | return; | |
233 | assert(pkg->priority <= PKG_PRIO_UNKNOWN); | |
234 | if (flags&fw_printheader) | |
235 | varbuf_add_str(vb, "Priority: "); | |
236 | varbuf_add_str(vb, pkg_priority_name(pkg)); | |
237 | if (flags&fw_printheader) | |
238 | varbuf_add_char(vb, '\n'); | |
239 | } | |
240 | ||
241 | void | |
242 | w_status(struct varbuf *vb, | |
243 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
244 | enum fwriteflags flags, const struct fieldinfo *fip) | |
245 | { | |
246 | if (pkgbin != &pkg->installed) | |
247 | return; | |
248 | assert(pkg->want <= PKG_WANT_PURGE); | |
249 | assert(pkg->eflag <= PKG_EFLAG_REINSTREQ); | |
250 | ||
251 | #define PEND pkg->trigpend_head | |
252 | #define AW pkg->trigaw.head | |
253 | switch (pkg->status) { | |
254 | case PKG_STAT_NOTINSTALLED: | |
255 | case PKG_STAT_CONFIGFILES: | |
256 | assert(!PEND); | |
257 | assert(!AW); | |
258 | break; | |
259 | case PKG_STAT_HALFINSTALLED: | |
260 | case PKG_STAT_UNPACKED: | |
261 | case PKG_STAT_HALFCONFIGURED: | |
262 | assert(!PEND); | |
263 | break; | |
264 | case PKG_STAT_TRIGGERSAWAITED: | |
265 | assert(AW); | |
266 | break; | |
267 | case PKG_STAT_TRIGGERSPENDING: | |
268 | assert(PEND); | |
269 | assert(!AW); | |
270 | break; | |
271 | case PKG_STAT_INSTALLED: | |
272 | assert(!PEND); | |
273 | assert(!AW); | |
274 | break; | |
275 | default: | |
276 | internerr("unknown package status '%d'", pkg->status); | |
277 | } | |
278 | #undef PEND | |
279 | #undef AW | |
280 | ||
281 | if (flags&fw_printheader) | |
282 | varbuf_add_str(vb, "Status: "); | |
283 | varbuf_add_str(vb, pkg_want_name(pkg)); | |
284 | varbuf_add_char(vb, ' '); | |
285 | varbuf_add_str(vb, pkg_eflag_name(pkg)); | |
286 | varbuf_add_char(vb, ' '); | |
287 | varbuf_add_str(vb, pkg_status_name(pkg)); | |
288 | if (flags&fw_printheader) | |
289 | varbuf_add_char(vb, '\n'); | |
290 | } | |
291 | ||
292 | void varbufdependency(struct varbuf *vb, struct dependency *dep) { | |
293 | struct deppossi *dop; | |
294 | const char *possdel; | |
295 | ||
296 | possdel= ""; | |
297 | for (dop= dep->list; dop; dop= dop->next) { | |
298 | assert(dop->up == dep); | |
299 | varbuf_add_str(vb, possdel); | |
300 | possdel = " | "; | |
301 | varbuf_add_str(vb, dop->ed->name); | |
302 | if (!dop->arch_is_implicit) | |
303 | varbuf_add_archqual(vb, dop->arch); | |
304 | if (dop->verrel != DPKG_RELATION_NONE) { | |
305 | varbuf_add_str(vb, " ("); | |
306 | switch (dop->verrel) { | |
307 | case DPKG_RELATION_EQ: | |
308 | varbuf_add_char(vb, '='); | |
309 | break; | |
310 | case DPKG_RELATION_GE: | |
311 | varbuf_add_str(vb, ">="); | |
312 | break; | |
313 | case DPKG_RELATION_LE: | |
314 | varbuf_add_str(vb, "<="); | |
315 | break; | |
316 | case DPKG_RELATION_GT: | |
317 | varbuf_add_str(vb, ">>"); | |
318 | break; | |
319 | case DPKG_RELATION_LT: | |
320 | varbuf_add_str(vb, "<<"); | |
321 | break; | |
322 | default: | |
323 | internerr("unknown dpkg_relation %d", dop->verrel); | |
324 | } | |
325 | varbuf_add_char(vb, ' '); | |
326 | varbufversion(vb,&dop->version,vdew_nonambig); | |
327 | varbuf_add_char(vb, ')'); | |
328 | } | |
329 | } | |
330 | } | |
331 | ||
332 | void | |
333 | w_dependency(struct varbuf *vb, | |
334 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
335 | enum fwriteflags flags, const struct fieldinfo *fip) | |
336 | { | |
337 | struct dependency *dyp; | |
338 | bool dep_found = false; | |
339 | ||
340 | for (dyp = pkgbin->depends; dyp; dyp = dyp->next) { | |
341 | if (dyp->type != fip->integer) continue; | |
342 | assert(dyp->up == pkg); | |
343 | ||
344 | if (dep_found) { | |
345 | varbuf_add_str(vb, ", "); | |
346 | } else { | |
347 | if (flags & fw_printheader) | |
348 | varbuf_add_fieldname(vb, fip); | |
349 | dep_found = true; | |
350 | } | |
351 | varbufdependency(vb,dyp); | |
352 | } | |
353 | if ((flags & fw_printheader) && dep_found) | |
354 | varbuf_add_char(vb, '\n'); | |
355 | } | |
356 | ||
357 | void | |
358 | w_conffiles(struct varbuf *vb, | |
359 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
360 | enum fwriteflags flags, const struct fieldinfo *fip) | |
361 | { | |
362 | struct conffile *i; | |
363 | ||
364 | if (!pkgbin->conffiles || pkgbin == &pkg->available) | |
365 | return; | |
366 | if (flags&fw_printheader) | |
367 | varbuf_add_str(vb, "Conffiles:\n"); | |
368 | for (i = pkgbin->conffiles; i; i = i->next) { | |
369 | if (i != pkgbin->conffiles) | |
370 | varbuf_add_char(vb, '\n'); | |
371 | varbuf_add_char(vb, ' '); | |
372 | varbuf_add_str(vb, i->name); | |
373 | varbuf_add_char(vb, ' '); | |
374 | varbuf_add_str(vb, i->hash); | |
375 | if (i->obsolete) | |
376 | varbuf_add_str(vb, " obsolete"); | |
377 | } | |
378 | if (flags&fw_printheader) | |
379 | varbuf_add_char(vb, '\n'); | |
380 | } | |
381 | ||
382 | void | |
383 | w_trigpend(struct varbuf *vb, | |
384 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
385 | enum fwriteflags flags, const struct fieldinfo *fip) | |
386 | { | |
387 | struct trigpend *tp; | |
388 | ||
389 | if (pkgbin == &pkg->available || !pkg->trigpend_head) | |
390 | return; | |
391 | ||
392 | assert(pkg->status >= PKG_STAT_TRIGGERSAWAITED && | |
393 | pkg->status <= PKG_STAT_TRIGGERSPENDING); | |
394 | ||
395 | if (flags & fw_printheader) | |
396 | varbuf_add_str(vb, "Triggers-Pending:"); | |
397 | for (tp = pkg->trigpend_head; tp; tp = tp->next) { | |
398 | varbuf_add_char(vb, ' '); | |
399 | varbuf_add_str(vb, tp->name); | |
400 | } | |
401 | if (flags & fw_printheader) | |
402 | varbuf_add_char(vb, '\n'); | |
403 | } | |
404 | ||
405 | void | |
406 | w_trigaw(struct varbuf *vb, | |
407 | const struct pkginfo *pkg, const struct pkgbin *pkgbin, | |
408 | enum fwriteflags flags, const struct fieldinfo *fip) | |
409 | { | |
410 | struct trigaw *ta; | |
411 | ||
412 | if (pkgbin == &pkg->available || !pkg->trigaw.head) | |
413 | return; | |
414 | ||
415 | assert(pkg->status > PKG_STAT_CONFIGFILES && | |
416 | pkg->status <= PKG_STAT_TRIGGERSAWAITED); | |
417 | ||
418 | if (flags & fw_printheader) | |
419 | varbuf_add_str(vb, "Triggers-Awaited:"); | |
420 | for (ta = pkg->trigaw.head; ta; ta = ta->sameaw.next) { | |
421 | varbuf_add_char(vb, ' '); | |
422 | varbuf_add_pkgbin_name(vb, ta->pend, &ta->pend->installed, pnaw_nonambig); | |
423 | } | |
424 | if (flags & fw_printheader) | |
425 | varbuf_add_char(vb, '\n'); | |
426 | } | |
427 | ||
428 | void | |
429 | varbuf_add_arbfield(struct varbuf *vb, const struct arbitraryfield *arbfield, | |
430 | enum fwriteflags flags) | |
431 | { | |
432 | if (flags & fw_printheader) { | |
433 | varbuf_add_str(vb, arbfield->name); | |
434 | varbuf_add_str(vb, ": "); | |
435 | } | |
436 | varbuf_add_str(vb, arbfield->value); | |
437 | if (flags & fw_printheader) | |
438 | varbuf_add_char(vb, '\n'); | |
439 | } | |
440 | ||
441 | void | |
442 | varbufrecord(struct varbuf *vb, | |
443 | const struct pkginfo *pkg, const struct pkgbin *pkgbin) | |
444 | { | |
445 | const struct fieldinfo *fip; | |
446 | const struct arbitraryfield *afp; | |
447 | ||
448 | for (fip= fieldinfos; fip->name; fip++) { | |
449 | fip->wcall(vb, pkg, pkgbin, fw_printheader, fip); | |
450 | } | |
451 | for (afp = pkgbin->arbs; afp; afp = afp->next) { | |
452 | varbuf_add_arbfield(vb, afp, fw_printheader); | |
453 | } | |
454 | } | |
455 | ||
456 | void | |
457 | writerecord(FILE *file, const char *filename, | |
458 | const struct pkginfo *pkg, const struct pkgbin *pkgbin) | |
459 | { | |
460 | struct varbuf vb = VARBUF_INIT; | |
461 | ||
462 | varbufrecord(&vb, pkg, pkgbin); | |
463 | varbuf_end_str(&vb); | |
464 | if (fputs(vb.buf,file) < 0) { | |
465 | struct varbuf pkgname = VARBUF_INIT; | |
466 | int errno_saved = errno; | |
467 | ||
468 | varbuf_add_pkgbin_name(&pkgname, pkg, pkgbin, pnaw_nonambig); | |
469 | ||
470 | errno = errno_saved; | |
471 | ohshite(_("failed to write details of '%.50s' to '%.250s'"), | |
472 | pkgname.buf, filename); | |
473 | } | |
474 | ||
475 | varbuf_destroy(&vb); | |
476 | } | |
477 | ||
478 | void | |
479 | writedb(const char *filename, enum writedb_flags flags) | |
480 | { | |
481 | static char writebuf[8192]; | |
482 | ||
483 | struct pkgiterator *iter; | |
484 | struct pkginfo *pkg; | |
485 | struct pkgbin *pkgbin; | |
486 | const char *which; | |
487 | struct atomic_file *file; | |
488 | struct varbuf vb = VARBUF_INIT; | |
489 | ||
490 | which = (flags & wdb_dump_available) ? "available" : "status"; | |
491 | ||
492 | file = atomic_file_new(filename, ATOMIC_FILE_BACKUP); | |
493 | atomic_file_open(file); | |
494 | if (setvbuf(file->fp, writebuf, _IOFBF, sizeof(writebuf))) | |
495 | ohshite(_("unable to set buffering on %s database file"), which); | |
496 | ||
497 | iter = pkg_db_iter_new(); | |
498 | while ((pkg = pkg_db_iter_next_pkg(iter)) != NULL) { | |
499 | pkgbin = (flags & wdb_dump_available) ? &pkg->available : &pkg->installed; | |
500 | /* Don't dump records which have no useful content. */ | |
501 | if (!pkg_is_informative(pkg, pkgbin)) | |
502 | continue; | |
503 | varbufrecord(&vb, pkg, pkgbin); | |
504 | varbuf_add_char(&vb, '\n'); | |
505 | varbuf_end_str(&vb); | |
506 | if (fputs(vb.buf, file->fp) < 0) | |
507 | ohshite(_("failed to write %s database record about '%.50s' to '%.250s'"), | |
508 | which, pkgbin_name(pkg, pkgbin, pnaw_nonambig), filename); | |
509 | varbuf_reset(&vb); | |
510 | } | |
511 | pkg_db_iter_free(iter); | |
512 | varbuf_destroy(&vb); | |
513 | if (flags & wdb_must_sync) | |
514 | atomic_file_sync(file); | |
515 | ||
516 | atomic_file_close(file); | |
517 | atomic_file_commit(file); | |
518 | atomic_file_free(file); | |
519 | ||
520 | if (flags & wdb_must_sync) | |
521 | dir_sync_path_parent(filename); | |
522 | } |