divertcmd.c 20 KB

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