chiark / gitweb /
dpkg (1.18.25) stretch; urgency=medium
[dpkg] / src / divertcmd.c
1 /*
2  * dpkg-divert - override a package's version of a file
3  *
4  * Copyright © 1995 Ian Jackson
5  * Copyright © 2000, 2001 Wichert Akkerman
6  * Copyright © 2006-2014 Guillem Jover <guillem@debian.org>
7  * Copyright © 2011 Linaro Limited
8  * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
22  */
23
24 #include <config.h>
25 #include <compat.h>
26
27 #include <sys/types.h>
28 #include <sys/stat.h>
29
30 #include <errno.h>
31 #if HAVE_LOCALE_H
32 #include <locale.h>
33 #endif
34 #include <fcntl.h>
35 #include <fnmatch.h>
36 #include <string.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39
40 #include <dpkg/i18n.h>
41 #include <dpkg/dpkg.h>
42 #include <dpkg/dpkg-db.h>
43 #include <dpkg/arch.h>
44 #include <dpkg/file.h>
45 #include <dpkg/glob.h>
46 #include <dpkg/buffer.h>
47 #include <dpkg/options.h>
48
49 #include "filesdb.h"
50
51
52 static const char printforhelp[] = N_(
53 "Use --help for help about diverting files.");
54
55 static const char *admindir;
56
57 static bool opt_pkgname_match_any = true;
58 static const char *opt_pkgname = NULL;
59 static const char *opt_divertto = NULL;
60
61 static int opt_verbose = 1;
62 static int opt_test = 0;
63 static int opt_rename = 0;
64
65
66 static void
67 printversion(const struct cmdinfo *cip, const char *value)
68 {
69         printf(_("Debian %s version %s.\n"), dpkg_get_progname(),
70                PACKAGE_RELEASE);
71
72         printf(_(
73 "This is free software; see the GNU General Public License version 2 or\n"
74 "later for copying conditions. There is NO warranty.\n"));
75
76         m_output(stdout, _("<standard output>"));
77
78         exit(0);
79 }
80
81 static void
82 usage(const struct cmdinfo *cip, const char *value)
83 {
84         printf(_(
85 "Usage: %s [<option> ...] <command>\n"
86 "\n"), dpkg_get_progname());
87
88         printf(_(
89 "Commands:\n"
90 "  [--add] <file>           add a diversion.\n"
91 "  --remove <file>          remove the diversion.\n"
92 "  --list [<glob-pattern>]  show file diversions.\n"
93 "  --listpackage <file>     show what package diverts the file.\n"
94 "  --truename <file>        return the diverted file.\n"
95 "\n"));
96
97         printf(_(
98 "Options:\n"
99 "  --package <package>      name of the package whose copy of <file> will not\n"
100 "                             be diverted.\n"
101 "  --local                  all packages' versions are diverted.\n"
102 "  --divert <divert-to>     the name used by other packages' versions.\n"
103 "  --rename                 actually move the file aside (or back).\n"
104 "  --admindir <directory>   set the directory with the diversions file.\n"
105 "  --test                   don't do anything, just demonstrate.\n"
106 "  --quiet                  quiet operation, minimal output.\n"
107 "  --help                   show this help message.\n"
108 "  --version                show the version.\n"
109 "\n"));
110
111         printf(_(
112 "When adding, default is --local and --divert <original>.distrib.\n"
113 "When removing, --package or --local and --divert must match if specified.\n"
114 "Package preinst/postrm scripts should always specify --package and --divert.\n"));
115
116         m_output(stdout, _("<standard output>"));
117
118         exit(0);
119 }
120
121 struct file {
122         const char *name;
123         enum {
124                 FILE_STAT_INVALID,
125                 FILE_STAT_VALID,
126                 FILE_STAT_NOFILE,
127         } stat_state;
128         struct stat stat;
129 };
130
131 static void
132 file_init(struct file *f, const char *filename)
133 {
134         f->name = filename;
135         f->stat_state = FILE_STAT_INVALID;
136 }
137
138 static void
139 file_stat(struct file *f)
140 {
141         int ret;
142
143         if (f->stat_state != FILE_STAT_INVALID)
144                 return;
145
146         ret = lstat(f->name, &f->stat);
147         if (ret && errno != ENOENT)
148                 ohshite(_("cannot stat file '%s'"), f->name);
149
150         if (ret == 0)
151                 f->stat_state = FILE_STAT_VALID;
152         else
153                 f->stat_state = FILE_STAT_NOFILE;
154 }
155
156 static void
157 check_writable_dir(struct file *f)
158 {
159         char *tmpname;
160         int tmpfd;
161
162         tmpname = str_fmt("%s%s", f->name, ".dpkg-divert.tmp");
163
164         tmpfd = creat(tmpname, 0600);
165         if (tmpfd < 0)
166                 ohshite(_("error checking '%s'"), f->name);
167         close(tmpfd);
168         (void)unlink(tmpname);
169
170         free(tmpname);
171 }
172
173 static bool
174 check_rename(struct file *src, struct file *dst)
175 {
176         file_stat(src);
177
178         /* If the source file is not present and we are not going to do
179          * the rename anyway there's no point in checking any further. */
180         if (src->stat_state == FILE_STAT_NOFILE)
181                 return false;
182
183         file_stat(dst);
184
185         /*
186          * Unfortunately we have to check for write access in both places,
187          * just having +w is not enough, since people do mount things RO,
188          * and we need to fail before we start mucking around with things.
189          * So we open a file with the same name as the diversions but with
190          * an extension that (hopefully) won't overwrite anything. If it
191          * succeeds, we assume a writable filesystem.
192          */
193
194         check_writable_dir(src);
195         check_writable_dir(dst);
196
197         if (src->stat_state == FILE_STAT_VALID &&
198             dst->stat_state == FILE_STAT_VALID &&
199             !(src->stat.st_dev == dst->stat.st_dev &&
200               src->stat.st_ino == dst->stat.st_ino))
201                 ohshit(_("rename involves overwriting '%s' with\n"
202                          "  different file '%s', not allowed"),
203                         dst->name, src->name);
204
205         return true;
206 }
207
208 static void
209 file_copy(const char *src, const char *dst)
210 {
211         struct dpkg_error err;
212         char *tmp;
213         int srcfd, dstfd;
214
215         srcfd = open(src, O_RDONLY);
216         if (srcfd < 0)
217                 ohshite(_("unable to open file '%s'"), src);
218
219         tmp = str_fmt("%s%s", dst, ".dpkg-divert.tmp");
220         dstfd = creat(tmp, 0600);
221         if (dstfd < 0)
222                 ohshite(_("unable to create file '%s'"), tmp);
223
224         push_cleanup(cu_filename, ~ehflag_normaltidy, NULL, 0, 1, tmp);
225
226         if (fd_fd_copy(srcfd, dstfd, -1, &err) < 0)
227                 ohshit(_("cannot copy '%s' to '%s': %s"), src, tmp, err.str);
228
229         close(srcfd);
230
231         if (fsync(dstfd))
232                 ohshite(_("unable to sync file '%s'"), tmp);
233         if (close(dstfd))
234                 ohshite(_("unable to close file '%s'"), tmp);
235
236         file_copy_perms(src, tmp);
237
238         if (rename(tmp, dst) != 0)
239                 ohshite(_("cannot rename '%s' to '%s'"), tmp, dst);
240
241         free(tmp);
242
243         pop_cleanup(ehflag_normaltidy);
244 }
245
246 static void
247 file_rename(struct file *src, struct file *dst)
248 {
249         if (src->stat_state == FILE_STAT_NOFILE)
250                 return;
251
252         if (dst->stat_state == FILE_STAT_VALID) {
253                 if (unlink(src->name))
254                         ohshite(_("rename: remove duplicate old link '%s'"),
255                                 src->name);
256         } else {
257                 if (rename(src->name, dst->name) == 0)
258                         return;
259
260                 /* If a rename didn't work try moving the file instead. */
261                 file_copy(src->name, dst->name);
262
263                 if (unlink(src->name))
264                         ohshite(_("unable to remove copied source file '%s'"),
265                                 src->name);
266         }
267 }
268
269 static void
270 diversion_check_filename(const char *filename)
271 {
272         if (filename[0] != '/')
273                 badusage(_("filename \"%s\" is not absolute"), filename);
274         if (strchr(filename, '\n') != NULL)
275                 badusage(_("file may not contain newlines"));
276 }
277
278 static const char *
279 diversion_pkg_name(struct diversion *d)
280 {
281         if (d->pkgset == NULL)
282                 return ":";
283         else
284                 return d->pkgset->name;
285 }
286
287 static const char *
288 varbuf_diversion(struct varbuf *str, const char *pkgname,
289                  const char *filename, const char *divertto)
290 {
291         varbuf_reset(str);
292
293         if (pkgname == NULL) {
294                 if (divertto == NULL)
295                         varbuf_printf(str, _("local diversion of %s"), filename);
296                 else
297                         varbuf_printf(str, _("local diversion of %s to %s"),
298                                       filename, divertto);
299         } else {
300                 if (divertto == NULL)
301                         varbuf_printf(str, _("diversion of %s by %s"),
302                                       filename, pkgname);
303                 else
304                         varbuf_printf(str, _("diversion of %s to %s by %s"),
305                                       filename, divertto, pkgname);
306         }
307
308         return str->buf;
309 }
310
311 static const char *
312 diversion_current(const char *filename)
313 {
314         static struct varbuf str = VARBUF_INIT;
315
316         if (opt_pkgname_match_any) {
317                 varbuf_reset(&str);
318
319                 if (opt_divertto == NULL)
320                         varbuf_printf(&str, _("any diversion of %s"), filename);
321                 else
322                         varbuf_printf(&str, _("any diversion of %s to %s"),
323                                       filename, opt_divertto);
324         } else {
325                 return varbuf_diversion(&str, opt_pkgname, filename, opt_divertto);
326         }
327
328         return str.buf;
329 }
330
331 static const char *
332 diversion_describe(struct diversion *d)
333 {
334         static struct varbuf str = VARBUF_INIT;
335         const char *pkgname;
336         const char *name_from, *name_to;
337
338         if (d->camefrom) {
339                 name_from = d->camefrom->name;
340                 name_to = d->camefrom->divert->useinstead->name;
341         } else {
342                 name_from = d->useinstead->divert->camefrom->name;
343                 name_to = d->useinstead->name;
344         }
345
346         if (d->pkgset == NULL)
347                 pkgname = NULL;
348         else
349                 pkgname = d->pkgset->name;
350
351         return varbuf_diversion(&str, pkgname, name_from, name_to);
352 }
353
354 static void
355 divertdb_write(void)
356 {
357         char *dbname;
358         struct atomic_file *file;
359         struct fileiterator *iter;
360         struct filenamenode *namenode;
361
362         dbname = dpkg_db_get_path(DIVERSIONSFILE);
363
364         file = atomic_file_new(dbname, ATOMIC_FILE_BACKUP);
365         atomic_file_open(file);
366
367         iter = files_db_iter_new();
368         while ((namenode = files_db_iter_next(iter))) {
369                 struct diversion *d = namenode->divert;
370
371                 if (d == NULL || d->useinstead == NULL)
372                         continue;
373
374                 fprintf(file->fp, "%s\n%s\n%s\n",
375                         d->useinstead->divert->camefrom->name,
376                         d->useinstead->name,
377                         diversion_pkg_name(d));
378         }
379         files_db_iter_free(iter);
380
381         atomic_file_sync(file);
382         atomic_file_close(file);
383         atomic_file_commit(file);
384         atomic_file_free(file);
385
386         free(dbname);
387 }
388
389 static bool
390 diversion_is_owned_by_self(struct pkgset *set, struct filenamenode *namenode)
391 {
392         struct pkginfo *pkg;
393         struct filepackages_iterator *iter;
394         bool owned = false;
395
396         if (set == NULL)
397                 return false;
398
399         for (pkg = &set->pkg; pkg; pkg = pkg->arch_next)
400                 ensure_packagefiles_available(pkg);
401
402         iter = filepackages_iter_new(namenode);
403         while ((pkg = filepackages_iter_next(iter))) {
404                 if (pkg->set == set) {
405                         owned = true;
406                         break;
407                 }
408         }
409         filepackages_iter_free(iter);
410
411         return owned;
412 }
413
414 static int
415 diversion_add(const char *const *argv)
416 {
417         const char *filename = argv[0];
418         struct file file_from, file_to;
419         struct diversion *contest, *altname;
420         struct filenamenode *fnn_from, *fnn_to;
421         struct pkgset *pkgset;
422
423         opt_pkgname_match_any = false;
424
425         /* Handle filename. */
426         if (!filename || argv[1])
427                 badusage(_("--%s needs a single argument"), cipaction->olong);
428
429         diversion_check_filename(filename);
430
431         file_init(&file_from, filename);
432         file_stat(&file_from);
433
434         if (file_from.stat_state == FILE_STAT_VALID &&
435             S_ISDIR(file_from.stat.st_mode))
436                 badusage(_("cannot divert directories"));
437
438         fnn_from = findnamenode(filename, 0);
439
440         /* Handle divertto. */
441         if (opt_divertto == NULL)
442                 opt_divertto = str_fmt("%s.distrib", filename);
443
444         if (strcmp(filename, opt_divertto) == 0)
445                 badusage(_("cannot divert file '%s' to itself"), filename);
446
447         file_init(&file_to, opt_divertto);
448
449         fnn_to = findnamenode(opt_divertto, 0);
450
451         /* Handle package name. */
452         if (opt_pkgname == NULL)
453                 pkgset = NULL;
454         else
455                 pkgset = pkg_db_find_set(opt_pkgname);
456
457         /* Check we are not stomping over an existing diversion. */
458         if (fnn_from->divert || fnn_to->divert) {
459                 if (fnn_to->divert && fnn_to->divert->camefrom &&
460                     strcmp(fnn_to->divert->camefrom->name, filename) == 0 &&
461                     fnn_from->divert && fnn_from->divert->useinstead &&
462                     strcmp(fnn_from->divert->useinstead->name, opt_divertto) == 0 &&
463                     fnn_from->divert->pkgset == pkgset) {
464                         if (opt_verbose > 0)
465                                 printf(_("Leaving '%s'\n"),
466                                        diversion_describe(fnn_from->divert));
467                         return 0;
468                 }
469
470                 ohshit(_("'%s' clashes with '%s'"),
471                        diversion_current(filename),
472                        fnn_from->divert ?
473                        diversion_describe(fnn_from->divert) :
474                        diversion_describe(fnn_to->divert));
475         }
476
477         /* Create new diversion. */
478         contest = nfmalloc(sizeof(*contest));
479         altname = nfmalloc(sizeof(*altname));
480
481         altname->camefrom = fnn_from;
482         altname->camefrom->divert = contest;
483         altname->useinstead = NULL;
484         altname->pkgset = pkgset;
485
486         contest->useinstead = fnn_to;
487         contest->useinstead->divert = altname;
488         contest->camefrom = NULL;
489         contest->pkgset = pkgset;
490
491         /* Update database and file system if needed. */
492         if (opt_verbose > 0)
493                 printf(_("Adding '%s'\n"), diversion_describe(contest));
494         if (opt_rename)
495                 opt_rename = check_rename(&file_from, &file_to);
496         /* Check we are not renaming a file owned by the diverting pkgset. */
497         if (opt_rename && diversion_is_owned_by_self(pkgset, fnn_from)) {
498                 if (opt_verbose > 0)
499                         printf(_("Ignoring request to rename file '%s' "
500                                  "owned by diverting package '%s'\n"),
501                                filename, pkgset->name);
502                 opt_rename = false;
503         }
504         if (!opt_test) {
505                 divertdb_write();
506                 if (opt_rename)
507                         file_rename(&file_from, &file_to);
508         }
509
510         return 0;
511 }
512
513 static bool
514 diversion_is_shared(struct pkgset *set, struct filenamenode *namenode)
515 {
516         const char *archname;
517         struct pkginfo *pkg;
518         struct dpkg_arch *arch;
519         struct filepackages_iterator *iter;
520         bool shared = false;
521
522         if (set == NULL)
523                 return false;
524
525         archname = getenv("DPKG_MAINTSCRIPT_ARCH");
526         arch = dpkg_arch_find(archname);
527         if (arch->type == DPKG_ARCH_NONE || arch->type == DPKG_ARCH_EMPTY)
528                 return false;
529
530         for (pkg = &set->pkg; pkg; pkg = pkg->arch_next)
531                 ensure_packagefiles_available(pkg);
532
533         iter = filepackages_iter_new(namenode);
534         while ((pkg = filepackages_iter_next(iter))) {
535                 if (pkg->set == set && pkg->installed.arch != arch) {
536                         shared = true;
537                         break;
538                 }
539         }
540         filepackages_iter_free(iter);
541
542         return shared;
543 }
544
545 static int
546 diversion_remove(const char *const *argv)
547 {
548         const char *filename = argv[0];
549         struct filenamenode *namenode;
550         struct diversion *contest, *altname;
551         struct file file_from, file_to;
552         struct pkgset *pkgset;
553
554         if (!filename || argv[1])
555                 badusage(_("--%s needs a single argument"), cipaction->olong);
556
557         diversion_check_filename(filename);
558
559         namenode = findnamenode(filename, fnn_nonew);
560
561         if (namenode == NULL || namenode->divert == NULL ||
562             namenode->divert->useinstead == NULL) {
563                 if (opt_verbose > 0)
564                         printf(_("No diversion '%s', none removed.\n"),
565                                diversion_current(filename));
566                 return 0;
567         }
568
569         if (opt_pkgname == NULL)
570                 pkgset = NULL;
571         else
572                 pkgset = pkg_db_find_set(opt_pkgname);
573
574         contest = namenode->divert;
575         altname = contest->useinstead->divert;
576
577         if (opt_divertto != NULL &&
578             strcmp(opt_divertto, contest->useinstead->name) != 0)
579                 ohshit(_("mismatch on divert-to\n"
580                          "  when removing '%s'\n"
581                          "  found '%s'"),
582                        diversion_current(filename),
583                        diversion_describe(contest));
584
585         if (!opt_pkgname_match_any && pkgset != contest->pkgset)
586                 ohshit(_("mismatch on package\n"
587                          "  when removing '%s'\n"
588                          "  found '%s'"),
589                        diversion_current(filename),
590                        diversion_describe(contest));
591
592         /* Ignore removal request if the diverted file is still owned
593          * by another package in the same set. */
594         if (diversion_is_shared(pkgset, namenode)) {
595                 if (opt_verbose > 0)
596                         printf(_("Ignoring request to remove shared diversion '%s'.\n"),
597                                diversion_describe(contest));
598                 return 0;
599         }
600
601         if (opt_verbose > 0)
602                 printf(_("Removing '%s'\n"), diversion_describe(contest));
603
604         file_init(&file_from, altname->camefrom->name);
605         file_init(&file_to, contest->useinstead->name);
606
607         /* Remove entries from database. */
608         contest->useinstead->divert = NULL;
609         altname->camefrom->divert = NULL;
610
611         if (opt_rename)
612                 opt_rename = check_rename(&file_to, &file_from);
613         if (opt_rename && !opt_test)
614                 file_rename(&file_to, &file_from);
615
616         if (!opt_test)
617                 divertdb_write();
618
619         return 0;
620 }
621
622 static int
623 diversion_list(const char *const *argv)
624 {
625         struct fileiterator *iter;
626         struct filenamenode *namenode;
627         struct glob_node *glob_list = NULL;
628         const char *pattern;
629
630         while ((pattern = *argv++))
631                 glob_list_prepend(&glob_list, m_strdup(pattern));
632
633         if (glob_list == NULL)
634                 glob_list_prepend(&glob_list, m_strdup("*"));
635
636         iter = files_db_iter_new();
637         while ((namenode = files_db_iter_next(iter))) {
638                 struct glob_node *g;
639                 struct diversion *contest = namenode->divert;
640                 struct diversion *altname;
641                 const char *pkgname;
642
643                 if (contest->useinstead == NULL)
644                         continue;
645
646                 altname = contest->useinstead->divert;
647
648                 pkgname = diversion_pkg_name(contest);
649
650                 for (g = glob_list; g; g = g->next) {
651                         if (fnmatch(g->pattern, pkgname, 0) == 0 ||
652                             fnmatch(g->pattern, contest->useinstead->name, 0) == 0 ||
653                             fnmatch(g->pattern, altname->camefrom->name, 0) == 0) {
654                                 printf("%s\n", diversion_describe(contest));
655                                 break;
656                         }
657                 }
658         }
659         files_db_iter_free(iter);
660
661         glob_list_free(glob_list);
662
663         return 0;
664 }
665
666 static int
667 diversion_truename(const char *const *argv)
668 {
669         const char *filename = argv[0];
670         struct filenamenode *namenode;
671
672         if (!filename || argv[1])
673                 badusage(_("--%s needs a single argument"), cipaction->olong);
674
675         diversion_check_filename(filename);
676
677         namenode = findnamenode(filename, fnn_nonew);
678
679         /* Print the given name if file is not diverted. */
680         if (namenode && namenode->divert->useinstead)
681                 printf("%s\n", namenode->divert->useinstead->name);
682         else
683                 printf("%s\n", filename);
684
685         return 0;
686 }
687
688 static int
689 diversion_listpackage(const char *const *argv)
690 {
691         const char *filename = argv[0];
692         struct filenamenode *namenode;
693
694         if (!filename || argv[1])
695                 badusage(_("--%s needs a single argument"), cipaction->olong);
696
697         diversion_check_filename(filename);
698
699         namenode = findnamenode(filename, fnn_nonew);
700
701         /* Print nothing if file is not diverted. */
702         if (namenode == NULL)
703                 return 0;
704
705         if (namenode->divert->pkgset == NULL)
706                 /* Indicate package is local using something not in package
707                  * namespace. */
708                 printf("LOCAL\n");
709         else
710                 printf("%s\n", namenode->divert->pkgset->name);
711
712         return 0;
713 }
714
715 static void
716 set_package(const struct cmdinfo *cip, const char *value)
717 {
718         opt_pkgname_match_any = false;
719
720         /* If value is NULL we are being called from --local. */
721         opt_pkgname = value;
722
723         if (opt_pkgname && strchr(opt_pkgname, '\n') != NULL)
724                 badusage(_("package may not contain newlines"));
725 }
726
727 static void
728 set_divertto(const struct cmdinfo *cip, const char *value)
729 {
730         opt_divertto = value;
731
732         if (opt_divertto[0] != '/')
733                 badusage(_("filename \"%s\" is not absolute"), opt_divertto);
734         if (strchr(opt_divertto, '\n') != NULL)
735                 badusage(_("divert-to may not contain newlines"));
736 }
737
738 static const struct cmdinfo cmdinfo_add =
739         ACTION("add",         0, 0, diversion_add);
740
741 static const struct cmdinfo cmdinfos[] = {
742         ACTION("add",         0, 0, diversion_add),
743         ACTION("remove",      0, 0, diversion_remove),
744         ACTION("list",        0, 0, diversion_list),
745         ACTION("listpackage", 0, 0, diversion_listpackage),
746         ACTION("truename",    0, 0, diversion_truename),
747
748         { "admindir",   0,   1,  NULL,         &admindir, NULL          },
749         { "divert",     0,   1,  NULL,         NULL,      set_divertto  },
750         { "package",    0,   1,  NULL,         NULL,      set_package   },
751         { "local",      0,   0,  NULL,         NULL,      set_package   },
752         { "quiet",      0,   0,  &opt_verbose, NULL,      NULL, 0       },
753         { "rename",     0,   0,  &opt_rename,  NULL,      NULL, 1       },
754         { "test",       0,   0,  &opt_test,    NULL,      NULL, 1       },
755         { "help",      '?',  0,  NULL,         NULL,      usage         },
756         { "version",    0,   0,  NULL,         NULL,      printversion  },
757         {  NULL,        0                                               }
758 };
759
760 int
761 main(int argc, const char * const *argv)
762 {
763         const char *env_pkgname;
764         int ret;
765
766         dpkg_locales_init(PACKAGE);
767         dpkg_program_init("dpkg-divert");
768         dpkg_options_parse(&argv, cmdinfos, printforhelp);
769
770         admindir = dpkg_db_set_dir(admindir);
771
772         env_pkgname = getenv("DPKG_MAINTSCRIPT_PACKAGE");
773         if (opt_pkgname_match_any && env_pkgname)
774                 set_package(NULL, env_pkgname);
775
776         if (!cipaction)
777                 setaction(&cmdinfo_add, NULL);
778
779         modstatdb_open(msdbrw_readonly);
780         filesdbinit();
781         ensure_diversions();
782
783         ret = cipaction->action(argv);
784
785         modstatdb_shutdown();
786
787         dpkg_program_done();
788
789         return ret;
790 }