diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 41 | ||||
-rw-r--r-- | src/Makefile.in | 539 | ||||
-rw-r--r-- | src/archives.c | 995 | ||||
-rw-r--r-- | src/archives.h | 71 | ||||
-rw-r--r-- | src/cleanup.c | 227 | ||||
-rw-r--r-- | src/configure.c | 663 | ||||
-rw-r--r-- | src/depcon.c | 464 | ||||
-rw-r--r-- | src/enquiry.c | 459 | ||||
-rw-r--r-- | src/errors.c | 132 | ||||
-rw-r--r-- | src/filesdb.c | 610 | ||||
-rw-r--r-- | src/filesdb.h | 148 | ||||
-rw-r--r-- | src/help.c | 480 | ||||
-rw-r--r-- | src/main.c | 563 | ||||
-rw-r--r-- | src/main.h | 223 | ||||
-rw-r--r-- | src/packages.c | 417 | ||||
-rw-r--r-- | src/processarc.c | 1055 | ||||
-rw-r--r-- | src/query.c | 553 | ||||
-rw-r--r-- | src/remove.c | 584 | ||||
-rw-r--r-- | src/select.c | 146 | ||||
-rw-r--r-- | src/update.c | 128 |
20 files changed, 8498 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 000000000..bdccc631f --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,41 @@ +## Process this file with automake to produce Makefile.in + +localedir = $(datadir)/locale +INCLUDES = \ + -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir)/intl \ + -DADMINDIR=\"$(admindir)\" \ + -I$(top_srcdir)/lib + + +bin_PROGRAMS = dpkg dpkg-query + +dpkg_SOURCES = \ + archives.c archives.h \ + cleanup.c \ + configure.c \ + depcon.c \ + enquiry.c \ + errors.c \ + filesdb.c filesdb.h \ + help.c \ + main.c main.h \ + packages.c \ + processarc.c \ + remove.c \ + select.c \ + update.c + +dpkg_LDADD = $(LIBINTL) ../lib/libdpkg.a $(ZLIB_LIBS) $(BZ2_LIBS) + +dpkg_query_SOURCES = \ + errors.c \ + filesdb.c filesdb.h \ + query.c + +dpkg_query_LDADD = $(LIBINTL) ../lib/libdpkg.a $(ZLIB_LIBS) $(BZ2_LIBS) + + +install-data-local: + $(mkdir_p) $(DESTDIR)$(admindir)/alternatives + $(mkdir_p) $(DESTDIR)$(admindir)/info + $(mkdir_p) $(DESTDIR)$(admindir)/updates diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 000000000..19db2106b --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,539 @@ +# Makefile.in generated by automake 1.8.5 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004 Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +SOURCES = $(dpkg_SOURCES) $(dpkg_query_SOURCES) + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +bin_PROGRAMS = dpkg$(EXEEXT) dpkg-query$(EXEEXT) +subdir = src +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/arch.m4 \ + $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/compiler.m4 \ + $(top_srcdir)/m4/funcs.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intdiv0.m4 $(top_srcdir)/m4/intmax.m4 \ + $(top_srcdir)/m4/inttypes-pri.m4 $(top_srcdir)/m4/inttypes.m4 \ + $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/lcmessage.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libs.m4 \ + $(top_srcdir)/m4/linker.m4 $(top_srcdir)/m4/longdouble.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/perl.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/printf-posix.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/signed.m4 $(top_srcdir)/m4/size_max.m4 \ + $(top_srcdir)/m4/stdint_h.m4 $(top_srcdir)/m4/types.m4 \ + $(top_srcdir)/m4/uintmax_t.m4 $(top_srcdir)/m4/ulonglong.m4 \ + $(top_srcdir)/m4/wchar_t.m4 $(top_srcdir)/m4/wint_t.m4 \ + $(top_srcdir)/m4/xsize.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +binPROGRAMS_INSTALL = $(INSTALL_PROGRAM) +PROGRAMS = $(bin_PROGRAMS) +am_dpkg_OBJECTS = archives.$(OBJEXT) cleanup.$(OBJEXT) \ + configure.$(OBJEXT) depcon.$(OBJEXT) enquiry.$(OBJEXT) \ + errors.$(OBJEXT) filesdb.$(OBJEXT) help.$(OBJEXT) \ + main.$(OBJEXT) packages.$(OBJEXT) processarc.$(OBJEXT) \ + remove.$(OBJEXT) select.$(OBJEXT) update.$(OBJEXT) +dpkg_OBJECTS = $(am_dpkg_OBJECTS) +am__DEPENDENCIES_1 = +dpkg_DEPENDENCIES = $(am__DEPENDENCIES_1) ../lib/libdpkg.a \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_dpkg_query_OBJECTS = errors.$(OBJEXT) filesdb.$(OBJEXT) \ + query.$(OBJEXT) +dpkg_query_OBJECTS = $(am_dpkg_query_OBJECTS) +dpkg_query_DEPENDENCIES = $(am__DEPENDENCIES_1) ../lib/libdpkg.a \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/archives.Po ./$(DEPDIR)/cleanup.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/configure.Po ./$(DEPDIR)/depcon.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/enquiry.Po ./$(DEPDIR)/errors.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/filesdb.Po ./$(DEPDIR)/help.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/main.Po ./$(DEPDIR)/packages.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/processarc.Po ./$(DEPDIR)/query.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/remove.Po ./$(DEPDIR)/select.Po \ +@AMDEP_TRUE@ ./$(DEPDIR)/update.Po +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SOURCES = $(dpkg_SOURCES) $(dpkg_query_SOURCES) +DIST_SOURCES = $(dpkg_SOURCES) $(dpkg_query_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMDEP_FALSE = @AMDEP_FALSE@ +AMDEP_TRUE = @AMDEP_TRUE@ +AMTAR = @AMTAR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@ +BZ2_CFLAGS = @BZ2_CFLAGS@ +BZ2_LIBS = @BZ2_LIBS@ +CATOBJEXT = @CATOBJEXT@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CURSES_LIBS = @CURSES_LIBS@ +CXX = @CXX@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DATADIRNAME = @DATADIRNAME@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GENCAT = @GENCAT@ +GLIBC21 = @GLIBC21@ +GMSGFMT = @GMSGFMT@ +HAVE_ASPRINTF = @HAVE_ASPRINTF@ +HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@ +HAVE_SNPRINTF = @HAVE_SNPRINTF@ +HAVE_WPRINTF = @HAVE_WPRINTF@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INSTOBJEXT = @INSTOBJEXT@ +INTLBISON = @INTLBISON@ +INTLLIBS = @INTLLIBS@ +INTLOBJS = @INTLOBJS@ +INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@ +LDFLAGS = @LDFLAGS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKINSTALLDIRS = @MKINSTALLDIRS@ +MSGFMT = @MSGFMT@ +MSGMERGE = @MSGMERGE@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +POSUB = @POSUB@ +RANLIB = @RANLIB@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SSD_LIBS = @SSD_LIBS@ +STRIP = @STRIP@ +USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WITH_DSELECT_FALSE = @WITH_DSELECT_FALSE@ +WITH_DSELECT_TRUE = @WITH_DSELECT_TRUE@ +WITH_START_STOP_DAEMON_FALSE = @WITH_START_STOP_DAEMON_FALSE@ +WITH_START_STOP_DAEMON_TRUE = @WITH_START_STOP_DAEMON_TRUE@ +XGETTEXT = @XGETTEXT@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_RANLIB = @ac_ct_RANLIB@ +ac_ct_STRIP = @ac_ct_STRIP@ +admindir = @admindir@ +am__fastdepCC_FALSE = @am__fastdepCC_FALSE@ +am__fastdepCC_TRUE = @am__fastdepCC_TRUE@ +am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@ +am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +datadir = @datadir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +localedir = $(datadir)/locale +INCLUDES = \ + -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir)/intl \ + -DADMINDIR=\"$(admindir)\" \ + -I$(top_srcdir)/lib + +dpkg_SOURCES = \ + archives.c archives.h \ + cleanup.c \ + configure.c \ + depcon.c \ + enquiry.c \ + errors.c \ + filesdb.c filesdb.h \ + help.c \ + main.c main.h \ + packages.c \ + processarc.c \ + remove.c \ + select.c \ + update.c + +dpkg_LDADD = $(LIBINTL) ../lib/libdpkg.a $(ZLIB_LIBS) $(BZ2_LIBS) +dpkg_query_SOURCES = \ + errors.c \ + filesdb.c filesdb.h \ + query.c + +dpkg_query_LDADD = $(LIBINTL) ../lib/libdpkg.a $(ZLIB_LIBS) $(BZ2_LIBS) +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \ + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \ + if test -f $$p \ + ; then \ + f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \ + $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \ + else :; fi; \ + done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; for p in $$list; do \ + f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \ + rm -f "$(DESTDIR)$(bindir)/$$f"; \ + done + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +dpkg$(EXEEXT): $(dpkg_OBJECTS) $(dpkg_DEPENDENCIES) + @rm -f dpkg$(EXEEXT) + $(LINK) $(dpkg_LDFLAGS) $(dpkg_OBJECTS) $(dpkg_LDADD) $(LIBS) +dpkg-query$(EXEEXT): $(dpkg_query_OBJECTS) $(dpkg_query_DEPENDENCIES) + @rm -f dpkg-query$(EXEEXT) + $(LINK) $(dpkg_query_LDFLAGS) $(dpkg_query_OBJECTS) $(dpkg_query_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/archives.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cleanup.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configure.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/depcon.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enquiry.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errors.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filesdb.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/packages.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/processarc.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/query.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/remove.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/select.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/update.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \ +@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +uninstall-info-am: + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(CTAGS_ARGS)$$tags$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \ + list='$(DISTFILES)'; for file in $$list; do \ + case $$file in \ + $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \ + $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \ + esac; \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkdir_p) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(mkdir_p) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +info: info-am + +info-am: + +install-data-am: install-data-local + +install-exec-am: install-binPROGRAMS + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-info-am + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-data-local install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ + tags uninstall uninstall-am uninstall-binPROGRAMS \ + uninstall-info-am + + +install-data-local: + $(mkdir_p) $(DESTDIR)$(admindir)/alternatives + $(mkdir_p) $(DESTDIR)$(admindir)/info + $(mkdir_p) $(DESTDIR)$(admindir)/updates +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/archives.c b/src/archives.c new file mode 100644 index 000000000..76d61edc3 --- /dev/null +++ b/src/archives.c @@ -0,0 +1,995 @@ +/* + * dpkg - main program for package management + * archives.c - actions that process archive files, mainly unpack + * + * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk> + * Copyright (C) 2000 Wichert Akkerman <wakkerma@debian.org> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> +#include <assert.h> +#include <time.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <obstack.h> +#define obstack_chunk_alloc m_malloc +#define obstack_chunk_free free + +#include <dpkg.h> +#include <dpkg-db.h> +#include <tarfn.h> +#include <myopt.h> + +#include "filesdb.h" +#include "main.h" +#include "archives.h" + +/* We shouldn't need anymore than 10 conflictors */ +struct pkginfo *conflictor[20]; +int cflict_index = 0; + +/* snprintf(3) doesn't work if format contains %.<nnn>s and an argument has + * invalid char for locale, then it returns -1. + * ohshite() is ok, but fd_fd_copy(), which is used in tarobject() in this + * file, is not ok, because + * - fd_fd_copy() == buffer_copy_setup() [include/dpkg.h] + * - buffer_copy_setup() uses varbufvprintf(&v, desc, al); [lib/mlib.c] + * - varbufvpprintf() fails and memory exausted, because it call + * fmt = "backend dpkg-deb during `%.255s' + * arg may contain some invalid char, for example, + * /usr/share/doc/console-tools/examples/unicode/\342\231\252\342\231\254 + * in console-tools. + * In this case, if user uses some locale which doesn't support \342\231..., + * vsnprintf() always return -1 and varbufextend() again and again + * and memory exausted and die. + * + * So, we need to escape invalid char, probably as in + * tar-1.13.19/lib/quotearg.c: quotearg_buffer_restyled() + * but here I escape all 8bit chars, in order to be simple. + * - ukai@debian.or.jp + */ +static char * +quote_filename(char *buf, int size, char *s) +{ + char *r = buf; + while (size > 0) { + switch (*s) { + case '\0': + *buf = '\0'; + return r; + case '\\': + *buf++ = '\\'; + *buf++ = '\\'; + size -= 2; + break; + default: + if (((*s)&0x80) == 0) { + *buf++ = *s++; + --size; + } else { + if (size > 4) { + sprintf(buf, "\\%03o", + *(unsigned char *)s); + size -= 4; + buf += 4; + s++; + } else { + /* buffer full */ + *buf = '\0'; /* XXX */ + return s; + } + } + } + } + *buf = '\0'; /* XXX */ + return r; + +} + +/* special routine to handle partial reads from the tarfile */ +static int safe_read(int fd, void *buf, int len) +{ + int r, have= 0; + char *p = (char *)buf; + while (have < len) { + if ((r= read(fd,p,len-have))==-1) { + if (errno==EINTR || errno==EAGAIN) continue; + return r; + } + if (r==0) + break; + have+= r; + p+= r; + } + return have; +} + +static struct obstack tar_obs; +static int tarobs_init= 0; + +/* ensure the obstack is properly initialized */ +static void ensureobstackinit(void) { + + if (!tarobs_init) { + obstack_init(&tar_obs); + tarobs_init= 1; + } +} + +/* destroy the obstack */ +static void destroyobstack(void) { + if (tarobs_init) { + obstack_free(&tar_obs, 0); + tarobs_init= 0; + } +} + +int filesavespackage(struct fileinlist *file, struct pkginfo *pkgtobesaved, + struct pkginfo *pkgbeinginstalled) { + struct pkginfo *divpkg, *thirdpkg; + struct filepackages *packageslump; + int i; + + debug(dbg_eachfiledetail,"filesavespackage file `%s' package %s", + file->namenode->name,pkgtobesaved->name); + /* A package can only be saved by a file or directory which is part + * only of itself - it must be neither part of the new package being + * installed nor part of any 3rd package (this is important so that + * shared directories don't stop packages from disappearing). + */ + /* Is the file in the package being installed ? If so then it can't save. + */ + if (file->namenode->flags & fnnf_new_inarchive) { + debug(dbg_eachfiledetail,"filesavespackage ... in new archive -- no save"); + return 0; + } + /* If the file is a contended one and it's overridden by either + * the package we're considering disappearing or the package + * we're installing then they're not actually the same file, so + * we can't disappear the package - it is saved by this file. + */ + if (file->namenode->divert && file->namenode->divert->useinstead) { + divpkg= file->namenode->divert->pkg; + if (divpkg == pkgtobesaved || divpkg == pkgbeinginstalled) { + debug(dbg_eachfiledetail,"filesavespackage ... diverted -- save!"); + return 1; + } + } + /* Look for a 3rd package which can take over the file (in case + * it's a directory which is shared by many packages. + */ + for (packageslump= file->namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + thirdpkg= packageslump->pkgs[i]; + debug(dbg_eachfiledetail, "filesavespackage ... also in %s", + thirdpkg->name); + /* Is this not the package being installed or the one being + * checked for disappearance ? + */ + if (thirdpkg == pkgbeinginstalled || thirdpkg == pkgtobesaved) continue; + /* If !fileslistvalid then we've already disappeared this one, so + * we shouldn't try to make it take over this shared directory. + */ + debug(dbg_eachfiledetail,"filesavespackage ... is 3rd package"); + + if (!thirdpkg->clientdata->fileslistvalid) { + debug(dbg_eachfiledetail, + _("process_archive ... already disappeared !")); + continue; + } + /* We've found a package that can take this file. */ + debug(dbg_eachfiledetail, "filesavespackage ... taken -- no save"); + return 0; + } + } + debug(dbg_eachfiledetail, "filesavespackage ... not taken -- save !"); + return 1; +} + +void cu_pathname(int argc, void **argv) { + ensure_pathname_nonexisting((char*)(argv[0])); +} + +int tarfileread(void *ud, char *buf, int len) { + struct tarcontext *tc= (struct tarcontext*)ud; + int r; + if ((r= safe_read(tc->backendpipe,buf,len)) == -1) + ohshite(_("error reading from dpkg-deb pipe")); + return r; +} + +int fnameidlu; +struct varbuf fnamevb; +struct varbuf fnametmpvb; +struct varbuf fnamenewvb; +struct packageinlist *deconfigure= 0; + +static time_t currenttime; + +static int does_replace(struct pkginfo *newpigp, + struct pkginfoperfile *newpifp, + struct pkginfo *oldpigp) { + struct dependency *dep; + + debug(dbg_depcon,"does_replace new=%s old=%s (%s)",newpigp->name, + oldpigp->name,versiondescribe(&oldpigp->installed.version, + vdew_always)); + for (dep= newpifp->depends; dep; dep= dep->next) { + if (dep->type != dep_replaces || dep->list->ed != oldpigp) continue; + debug(dbg_depcondetail,"does_replace ... found old, version %s", + versiondescribe(&dep->list->version,vdew_always)); + if (!versionsatisfied(&oldpigp->installed,dep->list)) continue; + debug(dbg_depcon,"does_replace ... yes"); + return 1; + } + debug(dbg_depcon,"does_replace ... no"); + return 0; +} + +static void newtarobject_utime(const char *path, struct TarInfo *ti) { + struct utimbuf utb; + utb.actime= currenttime; + utb.modtime= ti->ModTime; + if (utime(path,&utb)) + ohshite(_("error setting timestamps of `%.255s'"),ti->Name); +} + +static void newtarobject_allmodes(const char *path, struct TarInfo *ti, struct filestatoverride* statoverride) { + if (chown(path, + statoverride ? statoverride->uid : ti->UserID, + statoverride ? statoverride->gid : ti->GroupID)) + ohshite(_("error setting ownership of `%.255s'"),ti->Name); + if (chmod(path,(statoverride ? statoverride->mode : ti->Mode) & ~S_IFMT)) + ohshite(_("error setting permissions of `%.255s'"),ti->Name); + newtarobject_utime(path,ti); +} + +void setupfnamevbs(const char *filename) { + fnamevb.used= fnameidlu; + varbufaddstr(&fnamevb,filename); + varbufaddc(&fnamevb,0); + + fnametmpvb.used= fnameidlu; + varbufaddstr(&fnametmpvb,filename); + varbufaddstr(&fnametmpvb,DPKGTEMPEXT); + varbufaddc(&fnametmpvb,0); + + fnamenewvb.used= fnameidlu; + varbufaddstr(&fnamenewvb,filename); + varbufaddstr(&fnamenewvb,DPKGNEWEXT); + varbufaddc(&fnamenewvb,0); + + debug(dbg_eachfiledetail, "setupvnamevbs main=`%s' tmp=`%s' new=`%s'", + fnamevb.buf, fnametmpvb.buf, fnamenewvb.buf); +} + +int unlinkorrmdir(const char *filename) { + /* Returns 0 on success or -1 on failure, just like unlink & rmdir */ + int r, e; + + if (!rmdir(filename)) { + debug(dbg_eachfiledetail,"unlinkorrmdir `%s' rmdir OK",filename); + return 0; + } + + if (errno != ENOTDIR) { + e= errno; + debug(dbg_eachfiledetail,"unlinkorrmdir `%s' rmdir %s",filename,strerror(e)); + errno= e; return -1; + } + + r= unlink(filename); e= errno; + debug(dbg_eachfiledetail,"unlinkorrmdir `%s' unlink %s", + filename, r ? strerror(e) : "OK"); + errno= e; return r; +} + +int tarobject(struct TarInfo *ti) { + static struct varbuf conffderefn, hardlinkfn, symlinkfn; + const char *usename; + + struct tarcontext *tc= (struct tarcontext*)ti->UserData; + int statr, fd, i, existingdirectory; + size_t r; + struct stat stab, stabd; + char databuf[TARBLKSZ]; + struct fileinlist *nifd; + struct pkginfo *divpkg, *otherpkg; + struct filepackages *packageslump; + mode_t am; + + ensureobstackinit(); + + /* Append to list of files. + * The trailing / put on the end of names in tarfiles has already + * been stripped by TarExtractor (lib/tarfn.c). + */ + nifd= obstack_alloc(&tar_obs, sizeof(struct fileinlist)); + nifd->namenode= findnamenode(ti->Name, 0); + nifd->next= 0; *tc->newfilesp= nifd; tc->newfilesp= &nifd->next; + nifd->namenode->flags |= fnnf_new_inarchive; + + debug(dbg_eachfile, + "tarobject ti->Name=`%s' Mode=%lo owner=%u.%u Type=%d(%c)" + " ti->LinkName=`%s' namenode=`%s' flags=%o instead=`%s'", + ti->Name, (long)ti->Mode, (unsigned)ti->UserID, (unsigned)ti->GroupID, ti->Type, + ti->Type == '\0' ? '_' : + ti->Type >= '0' && ti->Type <= '6' ? "-hlcbdp"[ti->Type - '0'] : '?', + ti->LinkName, + nifd->namenode->name, nifd->namenode->flags, + nifd->namenode->divert && nifd->namenode->divert->useinstead + ? nifd->namenode->divert->useinstead->name : "<none>"); + + if (nifd->namenode->divert && nifd->namenode->divert->camefrom) { + divpkg= nifd->namenode->divert->pkg; + forcibleerr(fc_overwritediverted, + _("trying to overwrite `%.250s', which is the " + "diverted version of `%.250s'%.10s%.100s%.10s"), + nifd->namenode->name, + nifd->namenode->divert->camefrom->name, + divpkg ? _(" (package: ") : "", + divpkg ? divpkg->name : "", + divpkg ? ")" : ""); + } + + usename= namenodetouse(nifd->namenode,tc->pkg)->name + 1; /* Skip the leading `/' */ + + if (nifd->namenode->flags & fnnf_new_conff) { + /* If it's a conffile we have to extract it next to the installed + * version (ie, we do the usual link-following). + */ + if (conffderef(tc->pkg, &conffderefn, usename)) + usename= conffderefn.buf; + debug(dbg_conff,"tarobject fnnf_new_conff deref=`%s'",usename); + } + + setupfnamevbs(usename); + + statr= lstat(fnamevb.buf,&stab); + if (statr) { + /* The lstat failed. */ + if (errno != ENOENT && errno != ENOTDIR) + ohshite(_("unable to stat `%.255s' (which I was about to install)"),ti->Name); + /* OK, so it doesn't exist. + * However, it's possible that we were in the middle of some other + * backup/restore operation and were rudely interrupted. + * So, we see if we have .dpkg-tmp, and if so we restore it. + */ + if (rename(fnametmpvb.buf,fnamevb.buf)) { + if (errno != ENOENT && errno != ENOTDIR) + ohshite(_("unable to clean up mess surrounding `%.255s' before " + "installing another version"),ti->Name); + debug(dbg_eachfiledetail,"tarobject nonexistent"); + } else { + debug(dbg_eachfiledetail,"tarobject restored tmp to main"); + statr= lstat(fnamevb.buf,&stab); + if (statr) ohshite(_("unable to stat restored `%.255s' before installing" + " another version"), ti->Name); + } + } else { + debug(dbg_eachfiledetail,"tarobject already exists"); + } + + /* Check to see if it's a directory or link to one and we don't need to + * do anything. This has to be done now so that we don't die due to + * a file overwriting conflict. + */ + existingdirectory= 0; + switch (ti->Type) { + case SymbolicLink: + /* If it's already an existing directory, do nothing. */ + if (!statr && S_ISDIR(stab.st_mode)) { + debug(dbg_eachfiledetail,"tarobject SymbolicLink exists as directory"); + existingdirectory= 1; + } + break; + case Directory: + /* If it's already an existing directory, do nothing. */ + if (!stat(fnamevb.buf,&stabd) && S_ISDIR(stabd.st_mode)) { + debug(dbg_eachfiledetail,"tarobject Directory exists"); + existingdirectory= 1; + } + break; + case NormalFile0: case NormalFile1: + case CharacterDevice: case BlockDevice: case FIFO: + case HardLink: + break; + default: + ohshit(_("archive contained object `%.255s' of unknown type 0x%x"),ti->Name,ti->Type); + } + + if (!existingdirectory) { + for (packageslump= nifd->namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + otherpkg= packageslump->pkgs[i]; + if (otherpkg == tc->pkg) continue; + debug(dbg_eachfile, "tarobject ... found in %s",otherpkg->name); + if (nifd->namenode->divert && nifd->namenode->divert->useinstead) { + /* Right, so we may be diverting this file. This makes the conflict + * OK iff one of us is the diverting package (we don't need to + * check for both being the diverting package, obviously). + */ + divpkg= nifd->namenode->divert->pkg; + debug(dbg_eachfile, "tarobject ... diverted, divpkg=%s", + divpkg ? divpkg->name : "<none>"); + if (otherpkg == divpkg || tc->pkg == divpkg) continue; + } + /* Nope ? Hmm, file conflict, perhaps. Check Replaces. */ + if (otherpkg->clientdata->replacingfilesandsaid) continue; + /* Is the package with the conflicting file in the `config files + * only' state ? If so it must be a config file and we can + * silenty take it over. + */ + if (otherpkg->status == stat_configfiles) continue; + /* Perhaps we're removing a conflicting package ? */ + if (otherpkg->clientdata->istobe == itb_remove) continue; + if (does_replace(tc->pkg,&tc->pkg->available,otherpkg)) { + printf(_("Replacing files in old package %s ...\n"),otherpkg->name); + otherpkg->clientdata->replacingfilesandsaid= 1; + } else { + if (!statr && S_ISDIR(stab.st_mode)) { + forcibleerr(fc_overwritedir, _("trying to overwrite directory `%.250s' " + "in package %.250s with nondirectory"), + nifd->namenode->name,otherpkg->name); + } else { + /* WTA: At this point we are replacing something without a Replaces. + * if the new object is a directory and the previous object does not + * exist assume it's also a directory and don't complain + */ + if (! (statr && ti->Type==Directory)) + forcibleerr(fc_overwrite, + _("trying to overwrite `%.250s', which is also in package %.250s"), + nifd->namenode->name,otherpkg->name); + } + } + } + } + } + + /* Now, at this stage we want to make sure neither of .dpkg-new and .dpkg-tmp + * are hanging around. + */ + ensure_pathname_nonexisting(fnamenewvb.buf); + ensure_pathname_nonexisting(fnametmpvb.buf); + + if (existingdirectory) return 0; + + /* Now we start to do things that we need to be able to undo + * if something goes wrong. + */ + push_cleanup(cu_installnew,~ehflag_normaltidy, 0,0, 1,(void*)nifd); + + /* Extract whatever it is as .dpkg-new ... */ + switch (ti->Type) { + case NormalFile0: case NormalFile1: + /* We create the file with mode 0 to make sure nobody can do anything with + * it until we apply the proper mode, which might be a statoverride. + */ + fd= open(fnamenewvb.buf, (O_CREAT|O_EXCL|O_WRONLY), 0); + if (fd < 0) ohshite(_("unable to create `%.255s'"),ti->Name); + push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd); + debug(dbg_eachfiledetail,"tarobject NormalFile[01] open size=%lu", + (unsigned long)ti->Size); + { char fnamebuf[256]; + fd_fd_copy(tc->backendpipe, fd, ti->Size, _("backend dpkg-deb during `%.255s'"),quote_filename(fnamebuf,256,ti->Name)); + } + r= ti->Size % TARBLKSZ; + if (r > 0) r= safe_read(tc->backendpipe,databuf,TARBLKSZ - r); + if (nifd->namenode->statoverride) + debug(dbg_eachfile, "tarobject ... stat override, uid=%d, gid=%d, mode=%04o", + nifd->namenode->statoverride->uid, + nifd->namenode->statoverride->gid, + nifd->namenode->statoverride->mode); + if (fchown(fd, + nifd->namenode->statoverride ? nifd->namenode->statoverride->uid : ti->UserID, + nifd->namenode->statoverride ? nifd->namenode->statoverride->gid : ti->GroupID)) + ohshite(_("error setting ownership of `%.255s'"),ti->Name); + am=(nifd->namenode->statoverride ? nifd->namenode->statoverride->mode : ti->Mode) & ~S_IFMT; + if (fchmod(fd,am)) + ohshite(_("error setting permissions of `%.255s'"),ti->Name); + pop_cleanup(ehflag_normaltidy); /* fd= open(fnamenewvb.buf) */ + if (close(fd)) + ohshite(_("error closing/writing `%.255s'"),ti->Name); + newtarobject_utime(fnamenewvb.buf,ti); + break; + case FIFO: + if (mkfifo(fnamenewvb.buf,0)) + ohshite(_("error creating pipe `%.255s'"),ti->Name); + debug(dbg_eachfiledetail,"tarobject FIFO"); + newtarobject_allmodes(fnamenewvb.buf,ti, nifd->namenode->statoverride); + break; + case CharacterDevice: + if (mknod(fnamenewvb.buf,S_IFCHR, ti->Device)) + ohshite(_("error creating device `%.255s'"),ti->Name); + debug(dbg_eachfiledetail,"tarobject CharacterDevice"); + newtarobject_allmodes(fnamenewvb.buf,ti, nifd->namenode->statoverride); + break; + case BlockDevice: + if (mknod(fnamenewvb.buf,S_IFBLK, ti->Device)) + ohshite(_("error creating device `%.255s'"),ti->Name); + debug(dbg_eachfiledetail,"tarobject BlockDevice"); + newtarobject_allmodes(fnamenewvb.buf,ti, nifd->namenode->statoverride); + break; + case HardLink: + varbufreset(&hardlinkfn); + varbufaddstr(&hardlinkfn,instdir); varbufaddc(&hardlinkfn,'/'); + varbufaddstr(&hardlinkfn,ti->LinkName); varbufaddc(&hardlinkfn,0); + if (link(hardlinkfn.buf,fnamenewvb.buf)) + ohshite(_("error creating hard link `%.255s'"),ti->Name); + debug(dbg_eachfiledetail,"tarobject HardLink"); + newtarobject_allmodes(fnamenewvb.buf,ti, nifd->namenode->statoverride); + break; + case SymbolicLink: + /* We've already cheched for an existing directory. */ + if (symlink(ti->LinkName,fnamenewvb.buf)) + ohshite(_("error creating symbolic link `%.255s'"),ti->Name); + debug(dbg_eachfiledetail,"tarobject SymbolicLink creating"); +#ifdef HAVE_LCHOWN + if (lchown(fnamenewvb.buf, +#else + if (chown(fnamenewvb.buf, +#endif + nifd->namenode->statoverride ? nifd->namenode->statoverride->uid : ti->UserID, + nifd->namenode->statoverride ? nifd->namenode->statoverride->gid : ti->GroupID)) + ohshite(_("error setting ownership of symlink `%.255s'"),ti->Name); + break; + case Directory: + /* We've already checked for an existing directory. */ + if (mkdir(fnamenewvb.buf,0)) + ohshite(_("error creating directory `%.255s'"),ti->Name); + debug(dbg_eachfiledetail,"tarobject Directory creating"); + newtarobject_allmodes(fnamenewvb.buf,ti,nifd->namenode->statoverride); + break; + default: + internerr("bad tar type, but already checked"); + } + /* + * Now we have extracted the new object in .dpkg-new (or, if the + * file already exists as a directory and we were trying to extract + * a directory or symlink, we returned earlier, so we don't need + * to worry about that here). + */ + + /* First, check to see if it's a conffile. If so we don't install + * it now - we leave it in .dpkg-new for --configure to take care of + */ + if (nifd->namenode->flags & fnnf_new_conff) { + debug(dbg_conffdetail,"tarobject conffile extracted"); + nifd->namenode->flags |= fnnf_elide_other_lists; + return 0; + } + + /* Now we install it. If we can do an atomic overwrite we do so. + * If not we move aside the old file and then install the new. + * The backup file will be deleted later. + */ + if (statr) { /* Don't try to back it up if it didn't exist. */ + debug(dbg_eachfiledetail,"tarobject new - no backup"); + } else { + if (ti->Type == Directory || S_ISDIR(stab.st_mode)) { + /* One of the two is a directory - can't do atomic install. */ + debug(dbg_eachfiledetail,"tarobject directory, nonatomic"); + nifd->namenode->flags |= fnnf_no_atomic_overwrite; + if (rename(fnamevb.buf,fnametmpvb.buf)) + ohshite(_("unable to move aside `%.255s' to install new version"),ti->Name); + } else if (S_ISLNK(stab.st_mode)) { + /* We can't make a symlink with two hardlinks, so we'll have to copy it. + * (Pretend that making a copy of a symlink is the same as linking to it.) + */ + varbufreset(&symlinkfn); + do { + varbufextend(&symlinkfn); + r= readlink(fnamevb.buf,symlinkfn.buf,symlinkfn.size); + if (r<0) ohshite(_("unable to read link `%.255s'"),ti->Name); + } while (r == symlinkfn.size); + symlinkfn.used= r; varbufaddc(&symlinkfn,0); + if (symlink(symlinkfn.buf,fnametmpvb.buf)) + ohshite(_("unable to make backup symlink for `%.255s'"),ti->Name); +#ifdef HAVE_LCHOWN + if (lchown(fnametmpvb.buf,stab.st_uid,stab.st_gid)) +#else + if (chown(fnametmpvb.buf,stab.st_uid,stab.st_gid)) +#endif + ohshite(_("unable to chown backup symlink for `%.255s'"),ti->Name); + } else { + debug(dbg_eachfiledetail,"tarobject nondirectory, `link' backup"); + if (link(fnamevb.buf,fnametmpvb.buf)) + ohshite(_("unable to make backup link of `%.255s' before installing new version"), + ti->Name); + } + } + + if (rename(fnamenewvb.buf,fnamevb.buf)) + ohshite(_("unable to install new version of `%.255s'"),ti->Name); + + nifd->namenode->flags |= fnnf_elide_other_lists; + + debug(dbg_eachfiledetail,"tarobject done and installed"); + return 0; +} + +static int try_remove_can(struct deppossi *pdep, + struct pkginfo *fixbyrm, + const char *why) { + struct packageinlist *newdeconf; + + if (force_depends(pdep)) { + fprintf(stderr, _("dpkg: warning - " + "ignoring dependency problem with removal of %s:\n%s"), + fixbyrm->name, why); + return 1; + } else if (f_autodeconf) { + if (pdep->up->up->installed.essential) { + if (fc_removeessential) { + fprintf(stderr, _("dpkg: warning - considering deconfiguration of essential\n" + " package %s, to enable removal of %s.\n"), + pdep->up->up->name,fixbyrm->name); + } else { + fprintf(stderr, _("dpkg: no, %s is essential, will not deconfigure\n" + " it in order to enable removal of %s.\n"), + pdep->up->up->name,fixbyrm->name); + return 0; + } + } + pdep->up->up->clientdata->istobe= itb_deconfigure; + newdeconf= malloc(sizeof(struct packageinlist)); + newdeconf->next= deconfigure; + newdeconf->pkg= pdep->up->up; + deconfigure= newdeconf; + return 1; + } else { + fprintf(stderr, _("dpkg: no, cannot remove %s (--auto-deconfigure will help):\n%s"), + fixbyrm->name, why); + return 0; + } +} + +void check_conflict(struct dependency *dep, struct pkginfo *pkg, + const char *pfilename) { + struct pkginfo *fixbyrm; + struct deppossi *pdep, flagdeppossi; + struct varbuf conflictwhy, removalwhy; + struct dependency *providecheck; + + varbufinit(&conflictwhy); + varbufinit(&removalwhy); + + fixbyrm= 0; + if (depisok(dep, &conflictwhy, &fixbyrm, 0)) { + varbuffree(&conflictwhy); + varbuffree(&removalwhy); + return; + } + if (fixbyrm) { + ensure_package_clientdata(fixbyrm); + if (fixbyrm->clientdata->istobe == itb_installnew) { + fixbyrm= dep->up; + ensure_package_clientdata(fixbyrm); + } + if (((pkg->available.essential && fixbyrm->installed.essential) || + (((fixbyrm->want != want_install && fixbyrm->want != want_hold) || + does_replace(pkg,&pkg->available,fixbyrm)) && + (!fixbyrm->installed.essential || fc_removeessential)))) { + assert(fixbyrm->clientdata->istobe == itb_normal || fixbyrm->clientdata->istobe == itb_deconfigure); + fixbyrm->clientdata->istobe= itb_remove; + fprintf(stderr, _("dpkg: considering removing %s in favour of %s ...\n"), + fixbyrm->name, pkg->name); + if (fixbyrm->status != stat_installed) { + fprintf(stderr, + _("%s is not properly installed - ignoring any dependencies on it.\n"), + fixbyrm->name); + pdep= 0; + } else { + for (pdep= fixbyrm->installed.depended; + pdep; + pdep= pdep->nextrev) { + if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends) + continue; + if (depisok(pdep->up, &removalwhy, 0,0)) continue; + varbufaddc(&removalwhy,0); + if (!try_remove_can(pdep,fixbyrm,removalwhy.buf)) + break; + } + if (!pdep) { + /* If we haven't found a reason not to yet, let's look some more. */ + for (providecheck= fixbyrm->installed.depends; + providecheck; + providecheck= providecheck->next) { + if (providecheck->type != dep_provides) continue; + for (pdep= providecheck->list->ed->installed.depended; + pdep; + pdep= pdep->nextrev) { + if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends) + continue; + if (depisok(pdep->up, &removalwhy, 0,0)) continue; + varbufaddc(&removalwhy,0); + fprintf(stderr, _("dpkg" + ": may have trouble removing %s, as it provides %s ...\n"), + fixbyrm->name, providecheck->list->ed->name); + if (!try_remove_can(pdep,fixbyrm,removalwhy.buf)) + goto break_from_both_loops_at_once; + } + } + break_from_both_loops_at_once:; + } + } + if (!pdep && skip_due_to_hold(fixbyrm)) { + pdep= &flagdeppossi; + } + if (!pdep && (fixbyrm->eflag & eflagf_reinstreq)) { + if (fc_removereinstreq) { + fprintf(stderr, _("dpkg: package %s requires reinstallation, but will" + " remove anyway as you request.\n"), fixbyrm->name); + } else { + fprintf(stderr, _("dpkg: package %s requires reinstallation, " + "will not remove.\n"), fixbyrm->name); + pdep= &flagdeppossi; + } + } + if (!pdep) { + /* if this gets triggered, it means a package has > 10 conflicts/replaces + * pairs, which is the package's fault + */ + assert(cflict_index < (int)sizeof(conflictor)); + /* This conflict is OK - we'll remove the conflictor. */ + conflictor[cflict_index++]= fixbyrm; + varbuffree(&conflictwhy); varbuffree(&removalwhy); + fprintf(stderr, _("dpkg: yes, will remove %s in favour of %s.\n"), + fixbyrm->name, pkg->name); + return; + } + fixbyrm->clientdata->istobe= itb_normal; /* put it back */ + } + } + varbufaddc(&conflictwhy,0); + fprintf(stderr, _("dpkg: regarding %s containing %s:\n%s"), + pfilename, pkg->name, conflictwhy.buf); + if (!force_conflicts(dep->list)) + ohshit(_("conflicting packages - not installing %.250s"),pkg->name); + fprintf(stderr, _("dpkg: warning - ignoring conflict, may proceed anyway !\n")); + varbuffree(&conflictwhy); + + return; +} + +void cu_cidir(int argc, void **argv) { + char *cidir= (char*)argv[0]; + char *cidirrest= (char*)argv[1]; + cidirrest[-1]= 0; + ensure_pathname_nonexisting(cidir); +} + +void cu_fileslist(int argc, void **argv) { + destroyobstack(); +} + +void archivefiles(const char *const *argv) { + const char *volatile thisarg; + const char *const *volatile argp; + jmp_buf ejbuf; + int pi[2], fc, nfiles, c, i, r; + FILE *pf; + static struct varbuf findoutput; + const char **arglist; + char *p; + + modstatdb_init(admindir, + f_noact ? msdbrw_readonly + : cipaction->arg == act_avail ? msdbrw_write + : fc_nonroot ? msdbrw_write + : msdbrw_needsuperuser); + + checkpath(); + + if (f_recursive) { + + if (!*argv) + badusage(_("--%s --recursive needs at least one path argument"),cipaction->olong); + + m_pipe(pi); + if (!(fc= m_fork())) { + const char *const *ap; + char **narglist; + int i; + m_dup2(pi[1],1); close(pi[0]); close(pi[1]); + for (i=0, ap=argv; *ap; ap++, i++); + narglist= m_malloc(sizeof(char*)*(i+15)); + narglist[0]= strdup(FIND); + for (i=1, ap=argv; *ap; ap++, i++) { + if (strchr(FIND_EXPRSTARTCHARS,(*ap)[0])) { + char *a; + a= m_malloc(strlen(*ap)+10); + strcpy(a,"./"); + strcat(a,*ap); + narglist[i]= a; + } else { + narglist[i]= strdup(*ap); + } + } + narglist[i++]= strdup("-follow"); /* When editing these, make sure that */ + narglist[i++]= strdup("-name"); /* arglist is mallocd big enough, above. */ + narglist[i++]= strdup(ARCHIVE_FILENAME_PATTERN); + narglist[i++]= strdup("-type"); + narglist[i++]= strdup("f"); + narglist[i++]= strdup("-print0"); + narglist[i++]= 0; + execvp(FIND, narglist); + ohshite(_("failed to exec find for --recursive")); + } + close(pi[1]); + + nfiles= 0; + pf= fdopen(pi[0],"r"); if (!pf) ohshite(_("failed to fdopen find's pipe")); + varbufreset(&findoutput); + while ((c= fgetc(pf)) != EOF) { + varbufaddc(&findoutput,c); + if (!c) nfiles++; + } + if (ferror(pf)) ohshite(_("error reading find's pipe")); + if (fclose(pf)) ohshite(_("error closing find's pipe")); + r= waitsubproc(fc,"find",PROCNOERR); + if(!(r==0 || r==1)) + ohshit(_("find for --recursive returned unhandled error %i"),r); + + if (!nfiles) + ohshit(_("searched, but found no packages (files matching *.deb)")); + + varbufaddc(&findoutput,0); + varbufaddc(&findoutput,0); + + arglist= m_malloc(sizeof(char*)*(nfiles+1)); + p= findoutput.buf; i=0; + while (*p) { + arglist[i++]= p; + while ((c= *p++) != 0); + } + arglist[i]= 0; + argp= arglist; + + } else { + + if (!*argv) badusage(_("--%s needs at least one package archive file argument"), + cipaction->olong); + argp= argv; + + } + + currenttime= time(0); + + varbufinit(&fnamevb); + varbufinit(&fnametmpvb); + varbufinit(&fnamenewvb); + + varbufaddstr(&fnamevb,instdir); varbufaddc(&fnamevb,'/'); + varbufaddstr(&fnametmpvb,instdir); varbufaddc(&fnametmpvb,'/'); + varbufaddstr(&fnamenewvb,instdir); varbufaddc(&fnamenewvb,'/'); + fnameidlu= fnamevb.used; + + ensure_diversions(); + ensure_statoverrides(); + + while ((thisarg= *argp++) != 0) { + if (setjmp(ejbuf)) { + error_unwind(ehflag_bombout); + if (onerr_abort > 0) break; + continue; + } + push_error_handler(&ejbuf,print_error_perpackage,thisarg); + process_archive(thisarg); + onerr_abort++; + if (ferror(stdout)) werr("stdout"); + if (ferror(stderr)) werr("stderr"); + onerr_abort--; + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + } + + switch (cipaction->arg) { + case act_install: + case act_configure: + case act_remove: + case act_purge: + process_queue(); + case act_unpack: + case act_avail: + break; + default: + internerr("unknown action"); + } + + modstatdb_shutdown(); +} + +int wanttoinstall(struct pkginfo *pkg, const struct versionrevision *ver, int saywhy) { + /* Decide whether we want to install a new version of the package. + * ver is the version we might want to install. If saywhy is 1 then + * if we skip the package we say what we are doing (and, if we are + * selecting a previously deselected package, say so and actually do + * the select). want_install returns 0 if the package should be + * skipped and 1 if it should be installed. + * + * ver may be 0, in which case saywhy must be 0 and want_install may + * also return -1 to mean it doesn't know because it would depend on + * the version number. + */ + enum versiondisplayepochwhen needepochs; + int r; + + if (pkg->want != want_install && pkg->want != want_hold) { + if (f_alsoselect) { + if (saywhy) { + printf(_("Selecting previously deselected package %s.\n"),pkg->name); + pkg->want= want_install; + } + return 1; + } else { + if (saywhy) printf(_("Skipping deselected package %s.\n"),pkg->name); + return 0; + } + } + + if (pkg->status != stat_installed) return 1; + if (!ver) return -1; + + r= versioncompare(ver,&pkg->installed.version); + if (r > 0) { + return 1; + } else if (r == 0) { + if (f_skipsame && /* same version fully installed ? */ + pkg->status == stat_installed && !(pkg->eflag &= eflagf_reinstreq)) { + if (saywhy) fprintf(stderr, _("Version %.250s of %.250s already installed, " + "skipping.\n"), + versiondescribe(&pkg->installed.version,vdew_never), + pkg->name); + return 0; + } else { + return 1; + } + } else { + needepochs= epochsdiffer(&pkg->available.version,&pkg->installed.version) ? + vdew_always : vdew_never; + if (fc_downgrade) { + if (saywhy) fprintf(stderr, _("%s - warning: downgrading %.250s " + "from %.250s to %.250s.\n"), DPKG, pkg->name, + versiondescribe(&pkg->installed.version,needepochs), + versiondescribe(&pkg->available.version,needepochs)); + return 1; + } else { + if (saywhy) fprintf(stderr, _("Will not downgrade %.250s from version %.250s " + "to %.250s, skipping.\n"), pkg->name, + versiondescribe(&pkg->installed.version,needepochs), + versiondescribe(&pkg->available.version,needepochs)); + return 0; + } + } +} + +/* vi: ts=8 sw=2 + */ diff --git a/src/archives.h b/src/archives.h new file mode 100644 index 000000000..6141b6933 --- /dev/null +++ b/src/archives.h @@ -0,0 +1,71 @@ +/* + * dpkg - main program for package management + * archives.h - functions common to archives.c and processarc.c + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef ARCHIVES_H +#define ARCHIVES_H + +struct tarcontext { + int backendpipe; + struct pkginfo *pkg; + struct fileinlist **newfilesp; +}; + +extern int fnameidlu; +extern struct varbuf fnamevb; +extern struct varbuf fnametmpvb; +extern struct varbuf fnamenewvb; +extern struct packageinlist *deconfigure; + +extern struct pkginfo *conflictor[]; +extern int cflict_index; + +void cu_pathname(int argc, void **argv); +void cu_cidir(int argc, void **argv); +void cu_fileslist(int argc, void **argv); +void cu_backendpipe(int argc, void **argv); + +void cu_installnew(int argc, void **argv); + +void cu_prermupgrade(int argc, void **argv); +void cu_prerminfavour(int argc, void **argv); +void cu_preinstverynew(int argc, void **argv); +void cu_preinstnew(int argc, void **argv); +void cu_preinstupgrade(int argc, void **argv); +void cu_postrmupgrade(int argc, void **argv); + +void cu_prermdeconfigure(int argc, void **argv); +void ok_prermdeconfigure(int argc, void **argv); + +void setupfnamevbs(const char *filename); +int unlinkorrmdir(const char *filename); + +int tarobject(struct TarInfo *ti); +int tarfileread(void *ud, char *buf, int len); + +int filesavespackage(struct fileinlist*, struct pkginfo*, + struct pkginfo *pkgbeinginstalled); + +void check_conflict(struct dependency *dep, struct pkginfo *pkg, + const char *pfilename); + +extern int cleanup_pkg_failed, cleanup_conflictor_failed; + +#endif /* ARCHIVES_H */ diff --git a/src/cleanup.c b/src/cleanup.c new file mode 100644 index 000000000..3d93e8e42 --- /dev/null +++ b/src/cleanup.c @@ -0,0 +1,227 @@ +/* + * dpkg - main program for package management + * cleanup.c - cleanup functions, used when we need to unwind + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> +#include <assert.h> +#include <time.h> +#include <ctype.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <tarfn.h> +#include <myopt.h> + +#include "filesdb.h" +#include "main.h" +#include "archives.h" + +int cleanup_pkg_failed=0, cleanup_conflictor_failed=0; + +void cu_installnew(int argc, void **argv) { + /* Something went wrong and we're undoing. + * We have the following possible situations for non-conffiles: + * <foo>.dpkg-tmp exists - in this case we want to remove + * <foo> if it exists and replace it with <foo>.dpkg-tmp. + * This undoes the backup operation. We also make sure + * we delete <foo>.dpkg-new in case that's still hanging around. + * <foo>.dpkg-tmp does not exist - in this case we haven't + * got as far as creating it (or there wasn't an old version). + * In this case we just delete <foo>.dpkg-new if it exists, + * as it may be a half-extracted thing. + * For conffiles, we simply delete <foo>.dpkg-new. For these, + * <foo>.dpkg-tmp shouldn't exist, as we don't make a backup + * at this stage. Just to be on the safe side, though, we don't + * look for it. + */ + struct fileinlist *nifd= (struct fileinlist*)argv[0]; + struct filenamenode *namenode; + struct stat stab; + + cleanup_pkg_failed++; cleanup_conflictor_failed++; + + namenode= nifd->namenode; + debug(dbg_eachfile,"cu_installnew `%s' flags=%o",namenode->name,namenode->flags); + + setupfnamevbs(namenode->name); + + if (!(namenode->flags & fnnf_new_conff) && !lstat(fnametmpvb.buf,&stab)) { + /* OK, <foo>.dpkg-tmp exists. Remove <foo> and + * restore <foo>.dpkg-tmp ... + */ + if (namenode->flags & fnnf_no_atomic_overwrite) { + /* If we can't do an atomic overwrite we have to delete first any + * link to the new version we may have created. + */ + debug(dbg_eachfiledetail,"cu_installnew restoring nonatomic"); + if (unlinkorrmdir(fnamevb.buf) && errno != ENOENT && errno != ENOTDIR) + ohshite(_("unable to remove newly-installed version of `%.250s' to allow" + " reinstallation of backup copy"),namenode->name); + } else { + debug(dbg_eachfiledetail,"cu_installnew restoring atomic"); + } + /* Either we can do an atomic restore, or we've made room: */ + if (rename(fnametmpvb.buf,fnamevb.buf)) + ohshite(_("unable to restore backup version of `%.250s'"),namenode->name); + } else { + debug(dbg_eachfiledetail,"cu_installnew not restoring"); + } + /* Whatever, we delete <foo>.dpkg-new now, if it still exists. */ + if (unlinkorrmdir(fnamenewvb.buf) && errno != ENOENT && errno != ENOTDIR) + ohshite(_("unable to remove newly-extracted version of `%.250s'"),namenode->name); + + cleanup_pkg_failed--; cleanup_conflictor_failed--; +} + +void cu_prermupgrade(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + + if (cleanup_pkg_failed++) return; + maintainer_script_installed(pkg,POSTINSTFILE,"post-installation", + "abort-upgrade", + versiondescribe(&pkg->available.version, + vdew_nonambig), + (char*)0); + pkg->status= stat_installed; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} + +void ok_prermdeconfigure(int argc, void **argv) { + struct pkginfo *deconf= (struct pkginfo*)argv[0]; + /* also has conflictor in argv[1] and infavour in argv[2] */ + + if (cipaction->arg == act_install) + add_to_queue(deconf); +} + +void cu_prermdeconfigure(int argc, void **argv) { + struct pkginfo *deconf= (struct pkginfo*)argv[0]; + struct pkginfo *conflictor= (struct pkginfo*)argv[1]; + struct pkginfo *infavour= (struct pkginfo*)argv[2]; + + maintainer_script_installed(deconf,POSTINSTFILE,"post-installation", + "abort-deconfigure", "in-favour", infavour->name, + versiondescribe(&infavour->available.version, + vdew_nonambig), + "removing", conflictor->name, + versiondescribe(&conflictor->installed.version, + vdew_nonambig), + (char*)0); + deconf->status= stat_installed; + modstatdb_note(deconf); +} + +void cu_prerminfavour(int argc, void **argv) { + struct pkginfo *conflictor= (struct pkginfo*)argv[0]; + struct pkginfo *infavour= (struct pkginfo*)argv[1]; + + if (cleanup_conflictor_failed++) return; + maintainer_script_installed(conflictor,POSTINSTFILE,"post-installation", + "abort-remove", "in-favour", infavour->name, + versiondescribe(&infavour->available.version, + vdew_nonambig), + (char*)0); + conflictor->status= stat_installed; + conflictor->eflag &= ~eflagf_reinstreq; + modstatdb_note(conflictor); + cleanup_conflictor_failed--; +} + +void cu_preinstverynew(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + char *cidir= (char*)argv[1]; + char *cidirrest= (char*)argv[2]; + + if (cleanup_pkg_failed++) return; + maintainer_script_new(pkg->name, POSTRMFILE,"post-removal",cidir,cidirrest, + "abort-install",(char*)0); + pkg->status= stat_notinstalled; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} + +void cu_preinstnew(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + char *cidir= (char*)argv[1]; + char *cidirrest= (char*)argv[2]; + + if (cleanup_pkg_failed++) return; + maintainer_script_new(pkg->name, POSTRMFILE,"post-removal",cidir,cidirrest, + "abort-install", versiondescribe(&pkg->installed.version, + vdew_nonambig), + (char*)0); + pkg->status= stat_configfiles; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} + +void cu_preinstupgrade(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + char *cidir= (char*)argv[1]; + char *cidirrest= (char*)argv[2]; + enum pkgstatus *oldstatusp= (enum pkgstatus*)argv[3]; + + if (cleanup_pkg_failed++) return; + maintainer_script_new(pkg->name, POSTRMFILE,"post-removal",cidir,cidirrest, + "abort-upgrade", + versiondescribe(&pkg->installed.version, + vdew_nonambig), + (char*)0); + pkg->status= *oldstatusp; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} + +void cu_postrmupgrade(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + + if (cleanup_pkg_failed++) return; + maintainer_script_installed(pkg,PREINSTFILE,"pre-installation", + "abort-upgrade", versiondescribe(&pkg->available.version, + vdew_nonambig), + (char*)0); + cleanup_pkg_failed--; +} + +void cu_prermremove(int argc, void **argv) { + struct pkginfo *pkg= (struct pkginfo*)argv[0]; + + if (cleanup_pkg_failed++) return; + maintainer_script_installed(pkg,POSTINSTFILE,"post-installation", + "abort-remove", (char*)0); + pkg->status= stat_installed; + pkg->eflag &= ~eflagf_reinstreq; + modstatdb_note(pkg); + cleanup_pkg_failed--; +} diff --git a/src/configure.c b/src/configure.c new file mode 100644 index 000000000..efa7d2002 --- /dev/null +++ b/src/configure.c @@ -0,0 +1,663 @@ +/* + * dpkg - main program for package management + * configure.c - configure packages + * + * Copyright 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * Copyright 1999,2002 Wichert Akkerman <wichert@deephackmode.org> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <ctype.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <sys/wait.h> + +#include <dpkg.h> +#include <dpkg-db.h> + +#include "filesdb.h" +#include "main.h" + +int conffoptcells[2][2]= { CONFFOPTCELLS }; + +static void md5hash(struct pkginfo *pkg, char **hashbuf, const char *fn); +static void copyfileperm(const char* source, const char* target); +static void showdiff(const char* old, const char* new); +static void suspend(void); +static enum conffopt promptconfaction(const char* cfgfile, const char* realold, + const char* realnew, int useredited, int distedited, + enum conffopt what); + +extern struct pipef *status_pipes; + + +void deferred_configure(struct pkginfo *pkg) { + /* The algorithm for deciding what to configure first is as follows: + * Loop through all packages doing a `try 1' until we've been round + * and nothing has been done, then do `try 2' and `try 3' likewise. + * The incrementing of `dependtry' is done by process_queue(). + * Try 1: + * Are all dependencies of this package done ? If so, do it. + * Are any of the dependencies missing or the wrong version ? + * If so, abort (unless --force-depends, in which case defer) + * Will we need to configure a package we weren't given as an + * argument ? If so, abort - except if --force-configure-any, + * in which case we add the package to the argument list. + * If none of the above, defer the package. + * Try 2: + * Find a cycle and break it (see above). + * Do as for try 1. + * Try 3 (only if --force-depends-version). + * Same as for try 2, but don't mind version number in dependencies. + * Try 4 (only if --force-depends). + * Do anyway. + */ + struct varbuf aemsgs, cdr, cdr2; + char *cdr2rest; + int ok, r, useredited, distedited; + struct conffile *conff; + char *currenthash= 0, *newdisthash= 0; + struct stat stab; + enum conffopt what; + static const char *EMPTY_HASH = "-"; + + if (pkg->status == stat_notinstalled) + ohshit(_("no package named `%s' is installed, cannot configure"),pkg->name); + if (pkg->status == stat_installed) + ohshit(_("package %.250s is already installed and configured"), pkg->name); + if (pkg->status != stat_unpacked && pkg->status != stat_halfconfigured) + ohshit(_("package %.250s is not ready for configuration\n" + " cannot configure (current status `%.250s')"), + pkg->name, statusinfos[pkg->status].name); + + if (dependtry > 1) + if (findbreakcycle(pkg)) + sincenothing= 0; + + varbufinit(&aemsgs); + ok= dependencies_ok(pkg,0,&aemsgs); + if (ok == 1) { + varbuffree(&aemsgs); + pkg->clientdata->istobe= itb_installnew; + add_to_queue(pkg); + return; + } else if (ok == 0) { + sincenothing= 0; + varbufaddc(&aemsgs,0); + fprintf(stderr, + _("dpkg: dependency problems prevent configuration of %s:\n%s"), + pkg->name, aemsgs.buf); + varbuffree(&aemsgs); + ohshit(_("dependency problems - leaving unconfigured")); + } else if (aemsgs.used) { + varbufaddc(&aemsgs,0); + fprintf(stderr, + _("dpkg: %s: dependency problems, but configuring anyway as you request:\n%s"), + pkg->name, aemsgs.buf); + } + varbuffree(&aemsgs); + sincenothing= 0; + + if (pkg->eflag & eflagf_reinstreq) + forcibleerr(fc_removereinstreq, + _("Package is in a very bad inconsistent state - you should\n" + " reinstall it before attempting configuration.")); + + printf(_("Setting up %s (%s) ...\n"),pkg->name, + versiondescribe(&pkg->installed.version,vdew_never)); + + if (f_noact) { + pkg->status= stat_installed; + pkg->clientdata->istobe= itb_normal; + return; + } + + if (pkg->status == stat_unpacked) { + debug(dbg_general,"deferred_configure updating conffiles"); + /* This will not do at all the right thing with overridden conffiles + * or conffiles that are the `target' of an override; all the references + * here would be to the `contested' filename, and in any case there'd + * only be one hash for both `versions' of the conffile. + * + * Overriding conffiles is a silly thing to do anyway :-). + */ + + modstatdb_note(pkg); + + /* On entry, the `new' version of each conffile has been + * unpacked as *.dpkg-new, and the `installed' version is + * as-yet untouched in `*'. The hash of the `old distributed' + * version is in the conffiles data for the package. + * If `*.dpkg-new' no longer exists we assume that we've already + * processed this one. + */ + varbufinit(&cdr); + varbufinit(&cdr2); + for (conff= pkg->installed.conffiles; conff; conff= conff->next) { + r= conffderef(pkg, &cdr, conff->name); + if (r == -1) { + conff->hash= EMPTY_HASH; + continue; + } + md5hash(pkg,¤thash,cdr.buf); + + varbufreset(&cdr2); + varbufaddstr(&cdr2,cdr.buf); + cdr2.used+=50; varbufaddc(&cdr2,0); cdr2rest= cdr2.buf+strlen(cdr.buf); + /* From now on we can just strcpy(cdr2rest,extension); */ + + strcpy(cdr2rest,DPKGNEWEXT); + /* If the .dpkg-new file is no longer there, ignore this one. */ + if (lstat(cdr2.buf,&stab)) { + if (errno == ENOENT) continue; + ohshite(_("unable to stat new dist conffile `%.250s'"),cdr2.buf); + } + md5hash(pkg,&newdisthash,cdr2.buf); + + /* Copy the permissions from the installed version to the new + * distributed version. + */ + if (!stat(cdr.buf,&stab)) + copyfileperm(cdr.buf, cdr2.buf); + else if (errno != ENOENT) + ohshite(_("unable to stat current installed conffile `%.250s'"),cdr.buf); + + /* Select what the do */ + if (!strcmp(currenthash,newdisthash)) { + /* They're both the same so there's no point asking silly questions. */ + useredited= -1; + distedited= -1; + what= cfo_identical; + } else if (!strcmp(currenthash,NONEXISTENTFLAG) && fc_conff_miss) { + fprintf(stderr, _("\nConfiguration file `%s', does not exist on system.\n" + "Installing new config file as you request.\n"), conff->name); + what= cfo_newconff; + useredited= -1; + distedited= -1; + } else if (!strcmp(conff->hash,NEWCONFFILEFLAG)) { + if (!strcmp(currenthash,NONEXISTENTFLAG)) { + what= cfo_newconff; + useredited= -1; + distedited= -1; + } else { + useredited= 1; + distedited= 1; + what= conffoptcells[useredited][distedited] | cfof_isnew; + } + } else { + useredited= strcmp(conff->hash,currenthash) != 0; + distedited= strcmp(conff->hash,newdisthash) != 0; + what= conffoptcells[useredited][distedited]; + } + + debug(dbg_conff, + "deferred_configure `%s' (= `%s') useredited=%d distedited=%d what=%o", + conff->name, cdr.buf, useredited, distedited, what); + + what=promptconfaction(conff->name, cdr.buf, cdr2.buf, useredited, distedited, what); + + switch (what & ~cfof_isnew) { + case cfo_keep | cfof_backup: + strcpy(cdr2rest,DPKGOLDEXT); + if (unlink(cdr2.buf) && errno != ENOENT) + fprintf(stderr, + _("dpkg: %s: warning - failed to remove old backup `%.250s': %s\n"), + pkg->name, cdr2.buf, strerror(errno)); + cdr.used--; + varbufaddstr(&cdr,DPKGDISTEXT); + varbufaddc(&cdr,0); + strcpy(cdr2rest,DPKGNEWEXT); + if (rename(cdr2.buf,cdr.buf)) + fprintf(stderr, + _("dpkg: %s: warning - failed to rename `%.250s' to `%.250s': %s\n"), + pkg->name, cdr2.buf, cdr.buf, strerror(errno)); + break; + + case cfo_keep: + strcpy(cdr2rest,DPKGNEWEXT); + if (unlink(cdr2.buf)) + fprintf(stderr, + _("dpkg: %s: warning - failed to remove `%.250s': %s\n"), + pkg->name, cdr2.buf, strerror(errno)); + break; + + case cfo_install | cfof_backup: + strcpy(cdr2rest,DPKGDISTEXT); + if (unlink(cdr2.buf) && errno != ENOENT) + fprintf(stderr, + _("dpkg: %s: warning - failed to remove old distrib version `%.250s': %s\n"), + pkg->name, cdr2.buf, strerror(errno)); + strcpy(cdr2rest,DPKGOLDEXT); + if (unlink(cdr2.buf) && errno != ENOENT) + fprintf(stderr, + _("dpkg: %s: warning - failed to remove `%.250s' (before overwrite): %s\n"), + pkg->name, cdr2.buf, strerror(errno)); + if (link(cdr.buf,cdr2.buf)) + fprintf(stderr, + _("dpkg: %s: warning - failed to link `%.250s' to `%.250s': %s\n"), + pkg->name, cdr.buf, cdr2.buf, strerror(errno)); + /* fall through */ + case cfo_install: + printf(_("Installing new version of config file %s ...\n"),conff->name); + case cfo_newconff: + strcpy(cdr2rest,DPKGNEWEXT); + if (rename(cdr2.buf,cdr.buf)) + ohshite(_("unable to install `%.250s' as `%.250s'"),cdr2.buf,cdr.buf); + break; + + default: + internerr("unknown what"); + } + + conff->hash= nfstrsave(newdisthash); + modstatdb_note(pkg); + free(newdisthash); + free(currenthash); + + } /* for (conff= ... */ + varbuffree(&cdr); + varbuffree(&cdr2); + + pkg->status= stat_halfconfigured; + } + + assert(pkg->status == stat_halfconfigured); + + modstatdb_note(pkg); + + if (maintainer_script_installed(pkg, POSTINSTFILE, "post-installation", + "configure", + informativeversion(&pkg->configversion) + ? versiondescribe(&pkg->configversion, + vdew_nonambig) + : "", + (char*)0)) + putchar('\n'); + + pkg->status= stat_installed; + pkg->eflag= eflagv_ok; + modstatdb_note(pkg); +} + + + +/* Dereference a file by following all possibly used symlinks. + * Returns 0 if everything went ok, -1 otherwise. + */ +int conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in) { + static char* linkreadbuf = 0; + static int linkreadbufsize = 0; + struct stat stab; + int r, need; + int loopprotect; + + varbufreset(result); + varbufaddstr(result,instdir); + if (*in != '/') varbufaddc(result,'/'); + varbufaddstr(result,in); + varbufaddc(result,0); + + loopprotect= 0; + + for (;;) { + debug(dbg_conffdetail,"conffderef in=`%s' current working=`%s'", in, result->buf); + if (lstat(result->buf,&stab)) { + if (errno != ENOENT) + fprintf(stderr, _("dpkg: %s: warning - unable to stat config file `%s'\n" + " (= `%s'): %s\n"), + pkg->name, in, result->buf, strerror(errno)); + debug(dbg_conffdetail,"conffderef nonexistent"); + return 0; + } else if (S_ISREG(stab.st_mode)) { + debug(dbg_conff,"conffderef in=`%s' result=`%s'", in, result->buf); + return 0; + } else if (S_ISLNK(stab.st_mode)) { + debug(dbg_conffdetail,"conffderef symlink loopprotect=%d",loopprotect); + if (loopprotect++ >= 25) { + fprintf(stderr, _("dpkg: %s: warning - config file `%s' is a circular link\n" + " (= `%s')\n"), pkg->name, in, result->buf); + return -1; + } + need= 255; + for (;;) { + if (need > linkreadbufsize) { + linkreadbuf= m_realloc(linkreadbuf,need); + linkreadbufsize= need; + debug(dbg_conffdetail,"conffderef readlink realloc(%d)=%p",need,linkreadbuf); + } + r= readlink(result->buf,linkreadbuf,linkreadbufsize-1); + if (r < 0) { + fprintf(stderr, _("dpkg: %s: warning - unable to readlink conffile `%s'\n" + " (= `%s'): %s\n"), + pkg->name, in, result->buf, strerror(errno)); + return -1; + } + debug(dbg_conffdetail,"conffderef readlink gave %d, `%.*s'", + r, r>0 ? r : 0, linkreadbuf); + if (r < linkreadbufsize-1) break; + need= r<<2; + } + linkreadbuf[r]= 0; + if (linkreadbuf[0] == '/') { + varbufreset(result); + varbufaddstr(result,instdir); + debug(dbg_conffdetail,"conffderef readlink absolute"); + } else { + for (r=result->used-2; r>0 && result->buf[r] != '/'; r--) + ; + if (r < 0) { + fprintf(stderr, + _("dpkg: %s: warning - conffile `%.250s' resolves to degenerate filename\n" + " (`%s' is a symlink to `%s')\n"), + pkg->name, in, result->buf, linkreadbuf); + return -1; + } + if (result->buf[r] == '/') r++; + result->used= r; + debug(dbg_conffdetail,"conffderef readlink relative to `%.*s'", + (int)result->used, result->buf); + } + varbufaddstr(result,linkreadbuf); + varbufaddc(result,0); + } else { + fprintf(stderr, _("dpkg: %s: warning - conffile `%.250s' is not a plain" + " file or symlink (= `%s')\n"), + pkg->name, in, result->buf); + return -1; + } + } +} + +/* Generate a MD5 hash for fn and store it in *hashbuf. Memory is allocated + * by this function and should be freed manually. + */ +static void md5hash(struct pkginfo *pkg, char **hashbuf, const char *fn) { + static int fd; + + fd=open(fn,O_RDONLY); + + if (fd>=0) { + push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd); + fd_md5(fd, hashbuf, -1, _("md5hash")); + pop_cleanup(ehflag_normaltidy); /* fd= open(cdr.buf) */ + close(fd); + } else if (errno==ENOENT) { + *hashbuf= strdup(NONEXISTENTFLAG); + } else { + fprintf(stderr, _("dpkg: %s: warning - unable to open conffile %s for hash: %s\n"), + pkg->name, fn, strerror(errno)); + *hashbuf= strdup("-"); + } +} + +/* Copy file ownership and permissions from one file to another + */ +static void copyfileperm(const char* source, const char* target) { + struct stat stab; + + if (stat(source, &stab)==-1) { + if (errno==ENOENT) + return; + ohshite(_("unable to stat current installed conffile `%.250s'"), source); + } + + if (chown(target, stab.st_uid, stab.st_gid)==-1) + ohshite(_("unable to change ownership of new dist conffile `%.250s'"), target); + + if (chmod(target, (stab.st_mode & 07777))==-1) + ohshite(_("unable to set mode of new dist conffile `%.250s'"), target); +} + + + + +/* Show a diff between two files + */ +static void showdiff(const char* old, const char* new) { + int pid; + int r; + int status; + + if (!(pid=m_fork())) { + /* Child process */ + const char* p; /* pager */ + const char* s; /* shell */ + char cmdbuf[1024]; /* command to run */ + + p=getenv(PAGERENV); + if (!p || !*p) + p=DEFAULTPAGER; + + sprintf(cmdbuf, DIFF " -Nu %.250s %.250s | %.250s", old, new, p); + + s=getenv(SHELLENV); + if (!s || !*s) + s=DEFAULTSHELL; + + execlp(s,s,"-c", cmdbuf, NULL); + ohshite(_("failed to run %s (%.250s)"), DIFF, cmdbuf); + } + + /* Parent process */ + while (((r=waitpid(pid,&status,0))==-1) && (errno==EINTR)) + ; + + if (r!=pid) { + onerr_abort++; + ohshite(_("wait for shell failed")); + } +} + + +/* Suspend dpkg temporarily + */ +static void suspend(void) { + const char* s; + int pid; + + s= getenv(NOJOBCTRLSTOPENV); + if (s && *s) { + /* Do not job control to suspend but fork and start a new shell + * instead. + */ + + int status; /* waitpid status */ + int r; /* waitpid result */ + + fputs(_("Type `exit' when you're done.\n"), stderr); + + if (!(pid= m_fork())) { + /* Child process */ + s= getenv(SHELLENV); + if (!s || !*s) + s=DEFAULTSHELL; + + execlp(s,s,"-i",(char*)0); + ohshite(_("failed to exec shell (%.250s)"),s); + } + + /* Parent process */ + while (((r=waitpid(pid,&status,0))==-1) && (errno==EINTR)) + ; + + if (r!=pid) { + onerr_abort++; + ohshite(_("wait for shell failed")); + } + } else { + fputs(_("Don't forget to foreground (`fg') this " + "process when you're done !\n"), stderr); + kill(-getpgid(0),SIGTSTP); + } +} + + +/* Select what to do with a configuration file. + */ +static enum conffopt promptconfaction(const char* cfgfile, const char* realold, + const char* realnew, int useredited, int distedited, + enum conffopt what) { + const char *s; + int c, cc; + + if (!(what&cfof_prompt)) + return what; + + /* if there is a status pipe, send conffile-prompt there */ + if (status_pipes) { + static struct varbuf *status= NULL; + struct pipef *pipef= status_pipes; + int r; + if (status == NULL) { + status = nfmalloc(sizeof(struct varbuf)); + varbufinit(status); + } else + varbufreset(status); + + r= varbufprintf(status, "status: %s : %s : '%s' '%s' %i %i \n", + cfgfile, "conffile-prompt", + realold, realnew, useredited, distedited); + while (pipef) { + write(pipef->fd, status->buf, r); + pipef= pipef->next; + } + } + + + do { + fprintf(stderr, _("\nConfiguration file `%s'"), cfgfile); + if (strcmp(cfgfile, realold)) + fprintf(stderr,_(" (actually `%s')"), realold); + + if (what & cfof_isnew) { + fprintf(stderr, + _("\n" + " ==> File on system created by you or by a script.\n" + " ==> File also in package provided by package maintainer.\n")); + } else { + fprintf(stderr, useredited ? + _("\n ==> Modified (by you or by a script) since installation.\n") : + _("\n Not modified since installation.\n")); + + fprintf(stderr, distedited ? + _(" ==> Package distributor has shipped an updated version.\n") : + _(" Version in package is the same as at last installation.\n")); + } + + /* No --force-confdef but a forcible situtation */ + /* TODO: check if this condition can not be simplified to just !fc_conff_def */ + if (!(fc_conff_def && (what&(cfof_install|cfof_keep)))) { + if (fc_conff_new) { + fprintf(stderr, _(" ==> Using new file as you requested.\n")); + cc = 'y'; + break; + } else if (fc_conff_old) { + fprintf(stderr, _(" ==> Using current old file as you requested.\n")); + cc = 'n'; + break; + } + } + + + /* Force the default action (if there is one */ + if (fc_conff_def) { + if (what&cfof_keep) { + fprintf(stderr, _(" ==> Keeping old config file as default.\n")); + cc = 'n'; + break; + } else if (what&cfof_install) { + fprintf(stderr, _(" ==> Using new config file as default.\n")); + cc = 'y'; + break; + } + } + + + fprintf(stderr, + _(" What would you like to do about it ? Your options are:\n" + " Y or I : install the package maintainer's version\n" + " N or O : keep your currently-installed version\n" + " D : show the differences between the versions\n" + " Z : background this process to examine the situation\n")); + + if (what & cfof_keep) + fprintf(stderr, _(" The default action is to keep your current version.\n")); + else if (what & cfof_install) + fprintf(stderr, _(" The default action is to install the new version.\n")); + + s= strrchr(cfgfile,'/'); + if (!s || !*++s) s= cfgfile; + fprintf(stderr, "*** %s (Y/I/N/O/D/Z) %s ? ", + s, + (what & cfof_keep) ? _("[default=N]") : + (what & cfof_install) ? _("[default=Y]") : _("[no default]")); + + if (ferror(stderr)) + ohshite(_("error writing to stderr, discovered before conffile prompt")); + + cc= 0; + while ((c= getchar()) != EOF && c != '\n') + if (!isspace(c) && !cc) cc= tolower(c); + + if (c == EOF) { + if (ferror(stdin)) ohshite(_("read error on stdin at conffile prompt")); + ohshit(_("EOF on stdin at conffile prompt")); + } + + if (!cc) { + if (what & cfof_keep) { cc= 'n'; break; } + else if (what & cfof_install) { cc= 'y'; break; } + } + + /* fixme: say something if silently not install */ + if (cc == 'd') + showdiff(realold, realnew); + + if (cc == 'z') + suspend(); + + } while (!strchr("yino",cc)); + + + switch (cc) { + case 'i': + case 'y': + what=cfof_install|cfof_backup; + break; + + case 'n': + case 'o': + what=cfof_keep|cfof_backup; + break; + + default: + internerr("unknown response"); + } + + return what; +} diff --git a/src/depcon.c b/src/depcon.c new file mode 100644 index 000000000..0a725d104 --- /dev/null +++ b/src/depcon.c @@ -0,0 +1,464 @@ +/* + * dpkg - main program for package management + * depcon.c - dependency and conflict checking + * + * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <assert.h> + +#include <dpkg.h> +#include <dpkg-db.h> + +#include "main.h" + +struct cyclesofarlink { + struct cyclesofarlink *back; + struct pkginfo *pkg; + struct deppossi *possi; +}; + +static int findbreakcyclerecursive(struct pkginfo *pkg, struct cyclesofarlink *sofar); + +static int foundcyclebroken(struct cyclesofarlink *thislink, + struct cyclesofarlink *sofar, + struct pkginfo *dependedon, + struct deppossi *possi) { + struct cyclesofarlink *sol; + const char *postinstfilename; + struct stat stab; + + if(!possi) return 0; + /* We're investigating the dependency `possi' to see if it + * is part of a loop. To this end we look to see whether the + * depended-on package is already one of the packages whose + * dependencies we're searching. + */ + for (sol=sofar; sol && sol->pkg != dependedon; sol=sol->back); + + /* If not, we do a recursive search on it to see what we find. */ + if (!sol) return findbreakcyclerecursive(possi->ed,thislink); + + debug(dbg_depcon,"found cycle"); + /* Right, we now break one of the links. We prefer to break + * a dependency of a package without a postinst script, as + * this is a null operation. If this is not possible we break + * the other link in the recursive calling tree which mentions + * this package (this being the first package involved in the + * cycle). It doesn't particularly matter which we pick, but if + * we break the earliest dependency we came across we may be + * able to do something straight away when findbreakcycle returns. + */ + sofar= thislink; + for (sol= sofar; !(sol != sofar && sol->pkg == dependedon); sol=sol->back) { + postinstfilename= pkgadminfile(sol->pkg,POSTINSTFILE); + if (lstat(postinstfilename,&stab)) { + if (errno == ENOENT) break; + ohshite(_("unable to check for existence of `%.250s'"),postinstfilename); + } + } + /* Now we have either a package with no postinst, or the other + * occurrence of the current package in the list. + */ + sol->possi->cyclebreak= 1; + debug(dbg_depcon,"cycle broken at %s -> %s\n", + sol->possi->up->up->name, sol->possi->ed->name); + return 1; +} + +static int findbreakcyclerecursive(struct pkginfo *pkg, struct cyclesofarlink *sofar) { + /* Cycle breaking works recursively down the package dependency + * tree. `sofar' is the list of packages we've descended down + * already - if we encounter any of its packages again in a + * dependency we have found a cycle. + */ + struct cyclesofarlink thislink, *sol; + struct dependency *dep; + struct deppossi *possi, *providelink; + struct pkginfo *provider; + + if (pkg->color == black) + return 0; + pkg->color = gray; + + if (f_debug & dbg_depcondetail) { + fprintf(stderr,"D0%05o: findbreakcyclerecursive %s ",dbg_depcondetail,pkg->name); + for (sol=sofar; sol; sol=sol->back) fprintf(stderr," <- %s",sol->pkg->name); + fprintf(stderr,"\n"); + } + thislink.pkg= pkg; + thislink.back= sofar; + thislink.possi= 0; + for (dep= pkg->installed.depends; dep; dep= dep->next) { + if (dep->type != dep_depends && dep->type != dep_predepends) continue; + for (possi= dep->list; possi; possi= possi->next) { + /* Don't find the same cycles again. */ + if (possi->cyclebreak) continue; + thislink.possi= possi; + if (foundcyclebroken(&thislink,sofar,possi->ed,possi)) return 1; + /* Right, now we try all the providers ... */ + for (providelink= possi->ed->installed.depended; + providelink; + providelink= providelink->nextrev) { + if (providelink->up->type != dep_provides) continue; + provider= providelink->up->up; + if (provider->clientdata->istobe == itb_normal) continue; + /* We don't break things at `provides' links, so `possi' is + * still the one we use. + */ + if (foundcyclebroken(&thislink,sofar,provider,possi)) return 1; + if (foundcyclebroken(&thislink,sofar,possi->ed,provider->installed.depended)) return 1; + } + } + } + /* Nope, we didn't find a cycle to break. */ + pkg->color = black; + return 0; +} + +int findbreakcycle(struct pkginfo *pkg) { + struct pkgiterator *iter; + struct pkginfo *tpkg; + + /* Clear the visited flag of all packages before we traverse them. */ + for (iter = iterpkgstart(); (tpkg=iterpkgnext(iter)); ) { + tpkg->color = white; + } + + return findbreakcyclerecursive(pkg, 0); +} + +void describedepcon(struct varbuf *addto, struct dependency *dep) { + varbufaddstr(addto,dep->up->name); + switch (dep->type) { + case dep_depends: varbufaddstr(addto, _(" depends on ")); break; + case dep_predepends: varbufaddstr(addto, _(" pre-depends on ")); break; + case dep_recommends: varbufaddstr(addto, _(" recommends ")); break; + case dep_conflicts: varbufaddstr(addto, _(" conflicts with ")); break; + case dep_suggests: varbufaddstr(addto, _(" suggests ")); break; + case dep_enhances: varbufaddstr(addto, _(" enhances ")); break; + default: internerr("unknown deptype"); + } + varbufdependency(addto, dep); +} + +int depisok(struct dependency *dep, struct varbuf *whynot, + struct pkginfo **canfixbyremove, int allowunconfigd) { + /* *whynot must already have been initialised; it need not be + * empty though - it will be reset before use. + * If depisok returns 0 for `not OK' it will contain a description, + * newline-terminated BUT NOT NULL-TERMINATED, of the reason. + * If depisok returns 1 it will contain garbage. + * allowunconfigd should be non-zero during the `Pre-Depends' checking + * before a package is unpacked, when it is sufficient for the package + * to be unpacked provided that both the unpacked and previously-configured + * versions are acceptable. + */ + struct deppossi *possi; + struct deppossi *provider; + int nconflicts; + + /* Use this buffer so that when internationalisation comes along we + * don't have to rewrite the code completely, only redo the sprintf strings + * (assuming we have the fancy argument-number-specifiers). + * Allow 250x3 for package names, versions, &c, + 250 for ourselves. + */ + char linebuf[1024]; + + assert(dep->type == dep_depends || dep->type == dep_predepends || + dep->type == dep_conflicts || dep->type == dep_recommends || + dep->type == dep_suggests || dep->type == dep_enhances ); + + /* The dependency is always OK if we're trying to remove the depend*ing* + * package. + */ + switch (dep->up->clientdata->istobe) { + case itb_remove: case itb_deconfigure: + return 1; + case itb_normal: + /* Only `installed' packages can be make dependency problems */ + switch (dep->up->status) { + case stat_installed: + break; + case stat_notinstalled: case stat_configfiles: case stat_halfinstalled: + case stat_halfconfigured: case stat_unpacked: + return 1; + default: + internerr("unknown status depending"); + } + break; + case itb_installnew: case itb_preinstall: + break; + default: + internerr("unknown istobe depending"); + } + + /* Describe the dependency, in case we have to moan about it. */ + varbufreset(whynot); + varbufaddc(whynot, ' '); + describedepcon(whynot, dep); + varbufaddc(whynot,'\n'); + + /* TODO: check dep_enhances as well (WTA) */ + if (dep->type == dep_depends || dep->type == dep_predepends || + dep->type == dep_recommends || dep->type == dep_suggests ) { + + /* Go through the alternatives. As soon as we find one that + * we like, we return `1' straight away. Otherwise, when we get to + * the end we'll have accumulated all the reasons in whynot and + * can return `0'. + */ + + for (possi= dep->list; possi; possi= possi->next) { + switch (possi->ed->clientdata->istobe) { + case itb_remove: + sprintf(linebuf,_(" %.250s is to be removed.\n"),possi->ed->name); + break; + case itb_deconfigure: + sprintf(linebuf,_(" %.250s is to be deconfigured.\n"),possi->ed->name); + break; + case itb_installnew: + if (versionsatisfied(&possi->ed->available,possi)) return 1; + sprintf(linebuf,_(" %.250s is to be installed, but is version %.250s.\n"), + possi->ed->name, + versiondescribe(&possi->ed->available.version,vdew_nonambig)); + break; + case itb_normal: case itb_preinstall: + switch (possi->ed->status) { + case stat_installed: + if (versionsatisfied(&possi->ed->installed,possi)) return 1; + sprintf(linebuf,_(" %.250s is installed, but is version %.250s.\n"), + possi->ed->name, + versiondescribe(&possi->ed->installed.version,vdew_nonambig)); + break; + case stat_notinstalled: + /* Don't say anything about this yet - it might be a virtual package. + * Later on, if nothing has put anything in linebuf, we know that it + * isn't and issue a diagnostic then. + */ + *linebuf= 0; + break; + case stat_unpacked: + case stat_halfconfigured: + if (allowunconfigd) { + if (!informativeversion(&possi->ed->configversion)) { + sprintf(linebuf, _(" %.250s is unpacked, but has never been configured.\n"), + possi->ed->name); + break; + } else if (!versionsatisfied(&possi->ed->installed, possi)) { + sprintf(linebuf, _(" %.250s is unpacked, but is version %.250s.\n"), + possi->ed->name, + versiondescribe(&possi->ed->available.version,vdew_nonambig)); + break; + } else if (!versionsatisfied3(&possi->ed->configversion, + &possi->version,possi->verrel)) { + sprintf(linebuf, _(" %.250s latest configured version is %.250s.\n"), + possi->ed->name, + versiondescribe(&possi->ed->configversion,vdew_nonambig)); + break; + } else { + return 1; + } + } + default: + sprintf(linebuf, _(" %.250s is %s.\n"), + possi->ed->name, gettext(statusstrings[possi->ed->status])); + break; + } + break; + default: + internerr("unknown istobe depended"); + } + varbufaddstr(whynot, linebuf); + + /* If there was no version specified we try looking for Providers. */ + if (possi->verrel == dvr_none) { + + /* See if the package we're about to install Provides it. */ + for (provider= possi->ed->available.depended; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + if (provider->up->up->clientdata->istobe == itb_installnew) return 1; + } + + /* Now look at the packages already on the system. */ + for (provider= possi->ed->installed.depended; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + + switch (provider->up->up->clientdata->istobe) { + case itb_installnew: + /* Don't pay any attention to the Provides field of the + * currently-installed version of the package we're trying + * to install. We dealt with that by using the available + * information above. + */ + continue; + case itb_remove: + sprintf(linebuf, _(" %.250s provides %.250s but is to be removed.\n"), + provider->up->up->name, possi->ed->name); + break; + case itb_deconfigure: + sprintf(linebuf, _(" %.250s provides %.250s but is to be deconfigured.\n"), + provider->up->up->name, possi->ed->name); + break; + case itb_normal: case itb_preinstall: + if (provider->up->up->status == stat_installed) return 1; + sprintf(linebuf, _(" %.250s provides %.250s but is %s.\n"), + provider->up->up->name, possi->ed->name, + gettext(statusstrings[provider->up->up->status])); + break; + default: + internerr("unknown istobe provider"); + } + varbufaddstr(whynot, linebuf); + } + + if (!*linebuf) { + /* If the package wasn't installed at all, and we haven't said + * yet why this isn't satisfied, we should say so now. + */ + sprintf(linebuf, _(" %.250s is not installed.\n"), possi->ed->name); + varbufaddstr(whynot, linebuf); + } + } + } + + if (canfixbyremove) *canfixbyremove= 0; + return 0; + + } else { + + /* It's a conflict. There's only one main alternative, + * but we also have to consider Providers. We return `0' as soon + * as we find something that matches the conflict, and only describe + * it then. If we get to the end without finding anything we return `1'. + */ + + possi= dep->list; + nconflicts= 0; + + if (possi->ed != possi->up->up) { + /* If the package conflicts with itself it must mean that it conflicts + * with other packages which provide the same virtual name. We therefore + * don't look at the real package and go on to the virtual ones. + */ + + switch (possi->ed->clientdata->istobe) { + case itb_remove: + break; + case itb_installnew: + if (!versionsatisfied(&possi->ed->available, possi)) break; + sprintf(linebuf, _(" %.250s (version %.250s) is to be installed.\n"), + possi->ed->name, + versiondescribe(&possi->ed->available.version,vdew_nonambig)); + varbufaddstr(whynot, linebuf); + if (!canfixbyremove) return 0; + nconflicts++; + *canfixbyremove= possi->ed; + break; + case itb_normal: case itb_deconfigure: case itb_preinstall: + switch (possi->ed->status) { + case stat_notinstalled: case stat_configfiles: + break; + default: + if (!versionsatisfied(&possi->ed->installed, possi)) break; + sprintf(linebuf, _(" %.250s (version %.250s) is %s.\n"), + possi->ed->name, + versiondescribe(&possi->ed->installed.version,vdew_nonambig), + gettext(statusstrings[possi->ed->status])); + varbufaddstr(whynot, linebuf); + if (!canfixbyremove) return 0; + nconflicts++; + *canfixbyremove= possi->ed; + } + break; + default: + internerr("unknown istobe conflict"); + } + } + + /* If there was no version specified we try looking for Providers. */ + if (possi->verrel == dvr_none) { + + /* See if the package we're about to install Provides it. */ + for (provider= possi->ed->available.depended; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + if (provider->up->up->clientdata->istobe != itb_installnew) continue; + if (provider->up->up == dep->up) continue; /* conflicts and provides the same */ + sprintf(linebuf, _(" %.250s provides %.250s and is to be installed.\n"), + provider->up->up->name, possi->ed->name); + varbufaddstr(whynot, linebuf); + /* We can't remove the one we're about to install: */ + if (canfixbyremove) *canfixbyremove= 0; + return 0; + } + + /* Now look at the packages already on the system. */ + for (provider= possi->ed->installed.depended; + provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + + if (provider->up->up == dep->up) continue; /* conflicts and provides the same */ + + switch (provider->up->up->clientdata->istobe) { + case itb_installnew: + /* Don't pay any attention to the Provides field of the + * currently-installed version of the package we're trying + * to install. We dealt with that package by using the + * available information above. + */ + continue; + case itb_remove: + continue; + case itb_normal: case itb_deconfigure: case itb_preinstall: + switch (provider->up->up->status) { + case stat_notinstalled: case stat_configfiles: + continue; + default: + sprintf(linebuf, _(" %.250s provides %.250s and is %s.\n"), + provider->up->up->name, possi->ed->name, + gettext(statusstrings[provider->up->up->status])); + varbufaddstr(whynot, linebuf); + if (!canfixbyremove) return 0; + nconflicts++; + *canfixbyremove= provider->up->up; + break; + } + break; + default: + internerr("unknown istobe conflict provider"); + } + } + } + + if (!nconflicts) return 1; + if (nconflicts>1) *canfixbyremove= 0; + return 0; + + } /* if (dependency) {...} else {...} */ +} diff --git a/src/enquiry.c b/src/enquiry.c new file mode 100644 index 000000000..247394858 --- /dev/null +++ b/src/enquiry.c @@ -0,0 +1,459 @@ +/* + * dpkg - main program for package management + * enquiry.c - status enquiry and listing options + * + * Copyright (C) 1995,1996 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* fixme: per-package audit */ +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fnmatch.h> +#include <assert.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/termios.h> +#include <fcntl.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <myopt.h> + +#include "filesdb.h" +#include "main.h" + +int pkglistqsortcmp(const void *a, const void *b) { + const struct pkginfo *pa= *(const struct pkginfo**)a; + const struct pkginfo *pb= *(const struct pkginfo**)b; + return strcmp(pa->name,pb->name); +} + +static void limiteddescription(struct pkginfo *pkg, int maxl, + const char **pdesc_r, int *l_r) { + const char *pdesc, *p; + int l; + + pdesc= pkg->installed.valid ? pkg->installed.description : 0; + if (!pdesc) pdesc= _("(no description available)"); + p= strchr(pdesc,'\n'); + if (!p) p= pdesc+strlen(pdesc); + l= (p - pdesc > maxl) ? maxl : (int)(p - pdesc); + *pdesc_r=pdesc; *l_r=l; +} + +struct badstatinfo { + int (*yesno)(struct pkginfo*, const struct badstatinfo *bsi); + int val; + const char *explanation; +}; + +static int bsyn_reinstreq(struct pkginfo *pkg, const struct badstatinfo *bsi) { + return pkg->eflag &= eflagf_reinstreq; +} + +static int bsyn_status(struct pkginfo *pkg, const struct badstatinfo *bsi) { + if (pkg->eflag &= eflagf_reinstreq) return 0; + return (int)pkg->status == bsi->val; +} + +static const struct badstatinfo badstatinfos[]= { + { + bsyn_reinstreq, 0, + N_("The following packages are in a mess due to serious problems during\n" + "installation. They must be reinstalled for them (and any packages\n" + "that depend on them) to function properly:\n") + }, { + bsyn_status, stat_unpacked, + N_("The following packages have been unpacked but not yet configured.\n" + "They must be configured using dpkg --configure or the configure\n" + "menu option in dselect for them to work:\n") + }, { + bsyn_status, stat_halfconfigured, + N_("The following packages are only half configured, probably due to problems\n" + "configuring them the first time. The configuration should be retried using\n" + "dpkg --configure <package> or the configure menu option in dselect:\n") + }, { + bsyn_status, stat_halfinstalled, + N_("The following packages are only half installed, due to problems during\n" + "installation. The installation can probably be completed by retrying it;\n" + "the packages can be removed using dselect or dpkg --remove:\n") + }, { + 0 + } +}; + +static void describebriefly(struct pkginfo *pkg) { + int maxl, l; + const char *pdesc; + + maxl= 57; + l= strlen(pkg->name); + if (l>20) maxl -= (l-20); + limiteddescription(pkg,maxl,&pdesc,&l); + printf(" %-20s %.*s\n",pkg->name,l,pdesc); +} + +void audit(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + const struct badstatinfo *bsi; + int head; + + if (*argv) badusage(_("--audit does not take any arguments")); + + modstatdb_init(admindir,msdbrw_readonly); + + for (bsi= badstatinfos; bsi->yesno; bsi++) { + head= 0; + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + if (!bsi->yesno(pkg,bsi)) continue; + if (!head) { + fputs(gettext(bsi->explanation),stdout); + head= 1; + } + describebriefly(pkg); + } + iterpkgend(it); + if (head) putchar('\n'); + } + if (ferror(stderr)) werr("stderr"); +} + +struct sectionentry { + struct sectionentry *next; + const char *name; + int count; +}; + +static int yettobeunpacked(struct pkginfo *pkg, const char **thissect) { + if (pkg->want != want_install) return 0; + + switch (pkg->status) { + case stat_unpacked: case stat_installed: case stat_halfconfigured: + return 0; + case stat_notinstalled: case stat_halfinstalled: case stat_configfiles: + if (thissect) + *thissect= pkg->section && *pkg->section ? pkg->section : _("<unknown>"); + return 1; + default: + internerr("unknown status checking for unpackedness"); + } + return 0; +} + +void unpackchk(const char *const *argv) { + int totalcount, sects; + struct sectionentry *sectionentries, *se, **sep; + struct pkgiterator *it; + struct pkginfo *pkg; + const char *thissect; + char buf[20]; + int width; + + if (*argv) badusage(_("--yet-to-unpack does not take any arguments")); + + modstatdb_init(admindir,msdbrw_readonly|msdbrw_noavail); + + totalcount= 0; + sectionentries= 0; + sects= 0; + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + if (!yettobeunpacked(pkg, &thissect)) continue; + for (se= sectionentries; se && strcasecmp(thissect,se->name); se= se->next); + if (!se) { + se= nfmalloc(sizeof(struct sectionentry)); + for (sep= §ionentries; + *sep && strcasecmp(thissect,(*sep)->name) > 0; + sep= &(*sep)->next); + se->name= thissect; + se->count= 0; + se->next= *sep; + *sep= se; + sects++; + } + se->count++; totalcount++; + } + iterpkgend(it); + + if (totalcount == 0) exit(0); + + if (totalcount <= 12) { + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + if (!yettobeunpacked(pkg,0)) continue; + describebriefly(pkg); + } + iterpkgend(it); + } else if (sects <= 12) { + for (se= sectionentries; se; se= se->next) { + sprintf(buf,"%d",se->count); + printf(_(" %d in %s: "),se->count,se->name); + width= 70-strlen(se->name)-strlen(buf); + while (width > 59) { putchar(' '); width--; } + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + if (!yettobeunpacked(pkg,&thissect)) continue; + if (strcasecmp(thissect,se->name)) continue; + width -= strlen(pkg->name); width--; + if (width < 4) { printf(" ..."); break; } + printf(" %s",pkg->name); + } + iterpkgend(it); + putchar('\n'); + } + } else { + printf(_(" %d packages, from the following sections:"),totalcount); + width= 0; + for (se= sectionentries; se; se= se->next) { + sprintf(buf,"%d",se->count); + width -= (6 + strlen(se->name) + strlen(buf)); + if (width < 0) { putchar('\n'); width= 73 - strlen(se->name) - strlen(buf); } + printf(" %s (%d)",se->name,se->count); + } + putchar('\n'); + } + fflush(stdout); + if (ferror(stdout)) werr("stdout"); +} + +static void assertversion(const char *const *argv, + struct versionrevision *verrev_buf, + const char *reqversion) { + struct pkginfo *pkg; + + if (*argv) badusage(_("--assert-* does not take any arguments")); + + modstatdb_init(admindir,msdbrw_readonly|msdbrw_noavail); + if (verrev_buf->epoch == ~0UL) { + verrev_buf->epoch= 0; + verrev_buf->version= nfstrsave(reqversion); + verrev_buf->revision= 0; + } + pkg= findpackage("dpkg"); + switch (pkg->status) { + case stat_installed: + break; + case stat_unpacked: case stat_halfconfigured: case stat_halfinstalled: + if (versionsatisfied3(&pkg->configversion,verrev_buf,dvr_laterequal)) + break; + printf(_("Version of dpkg with working epoch support not yet configured.\n" + " Please use `dpkg --configure dpkg', and then try again.\n")); + exit(1); + default: + printf(_("dpkg not recorded as installed, cannot check for epoch support !\n")); + exit(1); + } +} + +void assertpredep(const char *const *argv) { + static struct versionrevision predepversion = {~0UL,0,0}; + assertversion(argv,&predepversion,"1.1.0"); +} + +void assertepoch(const char *const *argv) { + static struct versionrevision epochversion = {~0UL,0,0}; + assertversion(argv,&epochversion,"1.4.0.7"); +} + +void assertlongfilenames(const char *const *argv) { + static struct versionrevision epochversion = {~0UL,0,0}; + assertversion(argv,&epochversion,"1.4.1.17"); +} + +void assertmulticonrep(const char *const *argv) { + static struct versionrevision epochversion = {~0UL,0,0}; + assertversion(argv,&epochversion,"1.4.1.19"); +} + +void predeppackage(const char *const *argv) { + /* Print a single package which: + * (a) is the target of one or more relevant predependencies. + * (b) has itself no unsatisfied pre-dependencies. + * If such a package is present output is the Packages file entry, + * which can be massaged as appropriate. + * Exit status: + * 0 = a package printed, OK + * 1 = no suitable package available + * 2 = error + */ + static struct varbuf vb; + + struct pkgiterator *it; + struct pkginfo *pkg= 0, *startpkg, *trypkg; + struct dependency *dep; + struct deppossi *possi, *provider; + + if (*argv) badusage(_("--predep-package does not take any argument")); + + modstatdb_init(admindir,msdbrw_readonly); + clear_istobes(); /* We use clientdata->istobe to detect loops */ + + for (it=iterpkgstart(), dep=0; + !dep && (pkg=iterpkgnext(it)); + ) { + if (pkg->want != want_install) continue; /* Ignore packages user doesn't want */ + if (!pkg->files) continue; /* Ignore packages not available */ + pkg->clientdata->istobe= itb_preinstall; + for (dep= pkg->available.depends; dep; dep= dep->next) { + if (dep->type != dep_predepends) continue; + if (depisok(dep,&vb,0,1)) continue; + break; /* This will leave dep non-NULL, and so exit the loop. */ + } + pkg->clientdata->istobe= itb_normal; + /* If dep is NULL we go and get the next package. */ + } + if (!dep) exit(1); /* Not found */ + assert(pkg); + startpkg= pkg; + pkg->clientdata->istobe= itb_preinstall; + + /* OK, we have found an unsatisfied predependency. + * Now go and find the first thing we need to install, as a first step + * towards satisfying it. + */ + do { + /* We search for a package which would satisfy dep, and put it in pkg */ + for (possi=dep->list, pkg=0; + !pkg && possi; + possi=possi->next) { + trypkg= possi->ed; + if (!trypkg->available.valid) continue; + if (trypkg->files && versionsatisfied(&trypkg->available,possi)) { + if (trypkg->clientdata->istobe == itb_normal) { pkg= trypkg; break; } + } + if (possi->verrel != dvr_none) continue; + for (provider=possi->ed->available.depended; + !pkg && provider; + provider=provider->next) { + if (provider->up->type != dep_provides) continue; + trypkg= provider->up->up; + if (!trypkg->available.valid || !trypkg->files) continue; + if (trypkg->clientdata->istobe == itb_normal) { pkg= trypkg; break; } + } + } + if (!pkg) { + varbufreset(&vb); + describedepcon(&vb,dep); + varbufaddc(&vb,0); + fprintf(stderr, _("dpkg: cannot see how to satisfy pre-dependency:\n %s\n"),vb.buf); + ohshit(_("cannot satisfy pre-dependencies for %.250s (wanted due to %.250s)"), + dep->up->name,startpkg->name); + } + pkg->clientdata->istobe= itb_preinstall; + for (dep= pkg->available.depends; dep; dep= dep->next) { + if (dep->type != dep_predepends) continue; + if (depisok(dep,&vb,0,1)) continue; + break; /* This will leave dep non-NULL, and so exit the loop. */ + } + } while (dep); + + /* OK, we've found it - pkg has no unsatisfied pre-dependencies ! */ + writerecord(stdout,"<stdout>",pkg,&pkg->available); + if (fflush(stdout)) werr("stdout"); +} + +void printarch(const char *const *argv) { + if (*argv) badusage(_("--print-architecture does not take any argument")); + + if (printf("%s\n",architecture) == EOF) werr("stdout"); + if (fflush(stdout)) werr("stdout"); +} + +void cmpversions(const char *const *argv) { + struct relationinfo { + const char *string; + /* These values are exit status codes, so 0=true, 1=false */ + int if_lesser, if_equal, if_greater; + int if_none_a, if_none_both, if_none_b; + }; + + static const struct relationinfo relationinfos[]= { + /* < = > !a!2!b */ + { "le", 0,0,1, 0,0,1 }, + { "lt", 0,1,1, 0,1,1 }, + { "eq", 1,0,1, 1,0,1 }, + { "ne", 0,1,0, 0,1,0 }, + { "ge", 1,0,0, 1,0,0 }, + { "gt", 1,1,0, 1,1,0 }, + { "le-nl", 0,0,1, 1,0,0 }, /* Here none */ + { "lt-nl", 0,1,1, 1,1,0 }, /* is counted */ + { "ge-nl", 1,0,0, 0,0,1 }, /* than any version.*/ + { "gt-nl", 1,1,0, 0,1,1 }, /* */ + { "<", 0,0,1, 0,0,1 }, /* For compatibility*/ + { "<=", 0,0,1, 0,0,1 }, /* with dpkg */ + { "<<", 0,1,1, 0,1,1 }, /* control file */ + { "=", 1,0,1, 1,0,1 }, /* syntax */ + { ">", 1,0,0, 1,0,0 }, /* */ + { ">=", 1,0,0, 1,0,0 }, + { ">>", 1,1,0, 1,1,0 }, + { 0 } + }; + + const struct relationinfo *rip; + const char *emsg; + struct versionrevision a, b; + int r; + + if (!argv[0] || !argv[1] || !argv[2] || argv[3]) + badusage(_("--compare-versions takes three arguments:" + " <version> <relation> <version>")); + + for (rip=relationinfos; rip->string && strcmp(rip->string,argv[1]); rip++); + + if (!rip->string) badusage(_("--compare-versions bad relation")); + + if (*argv[0] && strcmp(argv[0],"<unknown>")) { + emsg= parseversion(&a,argv[0]); + if (emsg) { + if (printf(_("version a has bad syntax: %s\n"),emsg) == EOF) werr("stdout"); + if (fflush(stdout)) werr("stdout"); + exit(1); + } + } else { + blankversion(&a); + } + if (*argv[2] && strcmp(argv[2],"<unknown>")) { + emsg= parseversion(&b,argv[2]); + if (emsg) { + if (printf(_("version b has bad syntax: %s\n"),emsg) == EOF) werr("stdout"); + if (fflush(stdout)) werr("stdout"); + exit(1); + } + } else { + blankversion(&b); + } + if (!informativeversion(&a)) { + exit(informativeversion(&b) ? rip->if_none_a : rip->if_none_both); + } else if (!informativeversion(&b)) { + exit(rip->if_none_b); + } + r= versioncompare(&a,&b); + debug(dbg_general,"cmpversions a=`%s' b=`%s' r=%d", + versiondescribe(&a,vdew_always), + versiondescribe(&b,vdew_always), + r); + if (r>0) exit(rip->if_greater); + else if (r<0) exit(rip->if_lesser); + else exit(rip->if_equal); +} diff --git a/src/errors.c b/src/errors.c new file mode 100644 index 000000000..a830c2d1f --- /dev/null +++ b/src/errors.c @@ -0,0 +1,132 @@ +/* + * dpkg - main program for package management + * main.c - main program + * + * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <ctype.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <myopt.h> + +#include "main.h" + +int nerrs= 0; + +struct error_report { + struct error_report *next; + const char *what; +}; + +static struct error_report *reports=0; +static struct error_report **lastreport= &reports; +static struct error_report emergency; + +extern struct pipef *status_pipes; + +void print_error_perpackage(const char *emsg, const char *arg) { + struct error_report *nr; + + fprintf(stderr, _("%s: error processing %s (--%s):\n %s\n"), + DPKG, arg, cipaction->olong, emsg); + + if (status_pipes) { + static struct varbuf *status= NULL; + struct pipef *pipef= status_pipes; + int r; + if (status == NULL) { + status = nfmalloc(sizeof(struct varbuf)); + varbufinit(status); + } else + varbufreset(status); + + r= varbufprintf(status, "status: %s : %s : %s\n", arg, "error",emsg); + while (pipef) { + write(pipef->fd, status->buf, r); + pipef= pipef->next; + } + } + + + nr= malloc(sizeof(struct error_report)); + if (!nr) { + perror(_("dpkg: failed to allocate memory for new entry in list of failed packages.")); + onerr_abort++; + nr= &emergency; + } + nr->what= arg; + nr->next= 0; + *lastreport= nr; + lastreport= &nr->next; + + if (nerrs++ < errabort) return; + fprintf(stderr, _("dpkg: too many errors, stopping\n")); + onerr_abort++; +} + +int reportbroken_retexitstatus(void) { + if (reports) { + fputs(_("Errors were encountered while processing:\n"),stderr); + while (reports) { + fprintf(stderr," %s\n",reports->what); + reports= reports->next; + } + } + if (onerr_abort) { + fputs(_("Processing was halted because there were too many errors.\n"),stderr); + } + return nerrs || onerr_abort ? 1 : 0; +} + +int skip_due_to_hold(struct pkginfo *pkg) { + if (pkg->want != want_hold) return 0; + if (fc_hold) { + fprintf(stderr, _("Package %s was on hold, processing it anyway as you request\n"), + pkg->name); + return 0; + } + printf(_("Package %s is on hold, not touching it. Use --force-hold to override.\n"), + pkg->name); + return 1; +} + +void forcibleerr(int forceflag, const char *fmt, ...) { + va_list al; + va_start(al,fmt); + if (forceflag) { + fputs(_("dpkg - warning, overriding problem because --force enabled:\n "),stderr); + vfprintf(stderr,fmt,al); + fputc('\n',stderr); + } else { + ohshitv(fmt,al); + } + va_end(al); +} diff --git a/src/filesdb.c b/src/filesdb.c new file mode 100644 index 000000000..35f1d2ed5 --- /dev/null +++ b/src/filesdb.c @@ -0,0 +1,610 @@ +/* + * dpkg - main program for package management + * filesdb.c - management of database of files installed on system + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * Copyright (C) 2000,2001 Wichert Akkerman <wakkerma@debian.org> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <assert.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/stat.h> + +#include <pwd.h> +#include <grp.h> +#include <sys/types.h> + +#include <dpkg.h> +#include <dpkg-db.h> + +#include "filesdb.h" +#include "main.h" + +/*** Generic data structures and routines ***/ + +static int allpackagesdone= 0; +static int nfiles= 0; +static struct diversion *diversions= 0; +static FILE *diversionsfile= 0; +static FILE *statoverridefile= 0; + +void note_must_reread_files_inpackage(struct pkginfo *pkg) { + allpackagesdone= 0; + ensure_package_clientdata(pkg); + pkg->clientdata->fileslistvalid= 0; +} + +static int saidread=0; + + /* load the list of files in this package into memory, or update the + * list if it is there but stale + */ +void ensure_packagefiles_available(struct pkginfo *pkg) { + int fd; + const char *filelistfile; + struct fileinlist **lendp, *newent, *current; + struct filepackages *packageslump; + int search, findlast, putat; + struct stat stat_buf; + char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr; + + if (pkg->clientdata && pkg->clientdata->fileslistvalid) return; + ensure_package_clientdata(pkg); + + /* Throw away any the stale data, if there was any. */ + for (current= pkg->clientdata->files; + current; + current= current->next) { + /* For each file that used to be in the package, + * go through looking for this package's entry in the list + * of packages containing this file, and blank it out. + */ + for (packageslump= current->namenode->packages; + packageslump; + packageslump= packageslump->more) + for (search= 0; + search < PERFILEPACKAGESLUMP && packageslump->pkgs[search]; + search++) + if (packageslump->pkgs[search] == pkg) { + /* Hah! Found it. */ + for (findlast= search+1; + findlast < PERFILEPACKAGESLUMP && packageslump->pkgs[findlast]; + findlast++); + findlast--; + /* findlast is now the last occupied entry, which may be the same as + * search. We blank out the entry for this package. We also + * have to copy the last entry into the empty slot, because + * the list is null-pointer-terminated. + */ + packageslump->pkgs[search]= packageslump->pkgs[findlast]; + packageslump->pkgs[findlast]= 0; + /* This may result in an empty link in the list. This is OK. */ + goto xit_search_to_delete_from_perfilenodelist; + } + xit_search_to_delete_from_perfilenodelist: + ; + /* The actual filelist links were allocated using nfmalloc, so + * we shouldn't free them. + */ + } + pkg->clientdata->files= 0; + + /* Packages which aren't installed don't have a files list. */ + if (pkg->status == stat_notinstalled) { + pkg->clientdata->fileslistvalid= 1; return; + } + + filelistfile= pkgadminfile(pkg,LISTFILE); + + onerr_abort++; + + fd= open(filelistfile,O_RDONLY); + + if (fd==-1) { + if (errno != ENOENT) + ohshite(_("unable to open files list file for package `%.250s'"),pkg->name); + onerr_abort--; + if (pkg->status != stat_configfiles) { + if (saidread == 1) putc('\n',stderr); + fprintf(stderr, + _("dpkg: serious warning: files list file for package `%.250s' missing," + " assuming package has no files currently installed.\n"), pkg->name); + } + pkg->clientdata->files= 0; + pkg->clientdata->fileslistvalid= 1; + return; + } + + push_cleanup(cu_closefd,ehflag_bombout, 0,0, 1,&fd); + + if(fstat(fd, &stat_buf)) + ohshite("unable to stat files list file for package `%.250s'",pkg->name); + + if (stat_buf.st_size) { + loaded_list = nfmalloc(stat_buf.st_size); + loaded_list_end = loaded_list + stat_buf.st_size; + + fd_buf_copy(fd, loaded_list, stat_buf.st_size, _("files list for package `%.250s'"), pkg->name); + + lendp= &pkg->clientdata->files; + thisline = loaded_list; + while (thisline < loaded_list_end) { + if (!(ptr = memchr(thisline, '\n', loaded_list_end - thisline))) + ohshit("files list file for package `%.250s' is missing final newline",pkg->name); + /* where to start next time around */ + nextline = ptr + 1; + /* strip trailing "/" */ + if (ptr > thisline && ptr[-1] == '/') ptr--; + /* add the file to the list */ + if (ptr == thisline) + ohshit(_("files list file for package `%.250s' contains empty filename"),pkg->name); + *ptr = 0; + newent= nfmalloc(sizeof(struct fileinlist)); + newent->namenode= findnamenode(thisline, fnn_nocopy); + newent->next= 0; + *lendp= newent; + lendp= &newent->next; + thisline = nextline; + } + } + pop_cleanup(ehflag_normaltidy); /* fd= open() */ + if (close(fd)) + ohshite(_("error closing files list file for package `%.250s'"),pkg->name); + + onerr_abort--; + + for (newent= pkg->clientdata->files; newent; newent= newent->next) { + packageslump= newent->namenode->packages; + putat= 0; + if (packageslump) { + for (; putat < PERFILEPACKAGESLUMP && packageslump->pkgs[putat]; + putat++); + if (putat >= PERFILEPACKAGESLUMP) packageslump= 0; + } + if (!packageslump) { + packageslump= nfmalloc(sizeof(struct filepackages)); + packageslump->more= newent->namenode->packages; + newent->namenode->packages= packageslump; + putat= 0; + } + packageslump->pkgs[putat]= pkg; + if (++putat < PERFILEPACKAGESLUMP) packageslump->pkgs[putat]= 0; + } + pkg->clientdata->fileslistvalid= 1; +} + +void ensure_allinstfiles_available(void) { + struct pkgiterator *it; + struct pkginfo *pkg; + + if (allpackagesdone) return; + if (saidread<2) { + saidread=1; + printf(_("(Reading database ... ")); + } + it= iterpkgstart(); + while ((pkg= iterpkgnext(it)) != 0) ensure_packagefiles_available(pkg); + iterpkgend(it); + allpackagesdone= 1; + + if (saidread==1) { + printf(_("%d files and directories currently installed.)\n"),nfiles); + saidread=2; + } +} + +void ensure_allinstfiles_available_quiet(void) { + saidread=2; + ensure_allinstfiles_available(); +} + +void write_filelist_except(struct pkginfo *pkg, struct fileinlist *list, int leaveout) { + /* If leaveout is nonzero, will not write any file whose filenamenode + * has the fnnf_elide_other_lists flag set. + */ + static struct varbuf vb, newvb; + FILE *file; + + varbufreset(&vb); + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" INFODIR); + varbufaddstr(&vb,pkg->name); + varbufaddstr(&vb,"." LISTFILE); + varbufaddc(&vb,0); + + varbufreset(&newvb); + varbufaddstr(&newvb,vb.buf); + varbufaddstr(&newvb,NEWDBEXT); + varbufaddc(&newvb,0); + + file= fopen(newvb.buf,"w+"); + if (!file) + ohshite(_("unable to create updated files list file for package %s"),pkg->name); + push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)file); + while (list) { + if (!(leaveout && (list->namenode->flags & fnnf_elide_other_lists))) { + fputs(list->namenode->name,file); + putc('\n',file); + } + list= list->next; + } + if (ferror(file)) + ohshite(_("failed to write to updated files list file for package %s"),pkg->name); + if (fflush(file)) + ohshite(_("failed to flush updated files list file for package %s"),pkg->name); + if (fsync(fileno(file))) + ohshite(_("failed to sync updated files list file for package %s"),pkg->name); + pop_cleanup(ehflag_normaltidy); /* file= fopen() */ + if (fclose(file)) + ohshite(_("failed to close updated files list file for package %s"),pkg->name); + if (rename(newvb.buf,vb.buf)) + ohshite(_("failed to install updated files list file for package %s"),pkg->name); + + note_must_reread_files_inpackage(pkg); +} + +void reversefilelist_init(struct reversefilelistiter *iterptr, + struct fileinlist *files) { + /* Initialises an iterator that appears to go through the file + * list `files' in reverse order, returning the namenode from + * each. What actually happens is that we walk the list here, + * building up a reverse list, and then peel it apart one + * entry at a time. + */ + struct fileinlist *newent; + + iterptr->todo= 0; + while (files) { + newent= m_malloc(sizeof(struct fileinlist)); + newent->namenode= files->namenode; + newent->next= iterptr->todo; + iterptr->todo= newent; + files= files->next; + } +} + +struct filenamenode *reversefilelist_next(struct reversefilelistiter *iterptr) { + struct filenamenode *ret; + struct fileinlist *todo; + + todo= iterptr->todo; + if (!todo) return 0; + ret= todo->namenode; + iterptr->todo= todo->next; + free(todo); + return ret; +} + +void reversefilelist_abort(struct reversefilelistiter *iterptr) { + /* Clients must call this function to clean up the reversefilelistiter + * if they wish to break out of the iteration before it is all done. + * Calling this function is not necessary if reversefilelist_next has + * been called until it returned 0. + */ + while (reversefilelist_next(iterptr)); +} + +void ensure_statoverrides(void) { + static struct varbuf vb; + + struct stat stab1, stab2; + FILE *file; + char *loaded_list, *loaded_list_end, *thisline, *nextline, *ptr; + struct filestatoverride *fso; + struct filenamenode *fnn; + + varbufreset(&vb); + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" STATOVERRIDEFILE); + varbufaddc(&vb,0); + + onerr_abort++; + + file= fopen(vb.buf,"r"); + if (!file) { + if (errno != ENOENT) ohshite(_("failed to open statoverride file")); + if (!statoverridefile) { onerr_abort--; return; } + } else { + if (fstat(fileno(file),&stab2)) + ohshite(_("failed to fstat statoverride file")); + if (statoverridefile) { + if (fstat(fileno(statoverridefile),&stab1)) + ohshite(_("failed to fstat previous statoverride file")); + if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) { + fclose(file); onerr_abort--; return; + } + } + } + if (statoverridefile) fclose(statoverridefile); + statoverridefile= file; + setcloexec(fileno(statoverridefile), vb.buf); + + /* If the statoverride list is empty we don't need to bother reading it. */ + if (!stab2.st_size) { + onerr_abort--; + return; + } + + loaded_list = nfmalloc(stab2.st_size); + loaded_list_end = loaded_list + stab2.st_size; + + fd_buf_copy(fileno(file), loaded_list, stab2.st_size, _("statoverride file `%.250s'"), vb.buf); + + thisline = loaded_list; + while (thisline<loaded_list_end) { + char* endptr; + + fso= nfmalloc(sizeof(struct filestatoverride)); + + if (!(ptr = memchr(thisline, '\n', loaded_list_end - thisline))) + ohshit("statoverride file is missing final newline"); + /* where to start next time around */ + nextline = ptr + 1; + if (ptr == thisline) + ohshit(_("statoverride file contains empty line")); + *ptr = 0; + + /* Extract the uid */ + if (!(ptr=memchr(thisline, ' ', nextline-thisline))) + ohshit("syntax error in statusoverride file "); + *ptr=0; + if (thisline[0]=='#') { + fso->uid=strtol(thisline + 1, &endptr, 10); + if (*endptr!=0) + ohshit("syntax error: invalid uid in statusoverride file "); + } else { + struct passwd* pw = getpwnam(thisline); + if (pw==NULL) + ohshit("syntax error: unknown user `%s' in statusoverride file ", thisline); + fso->uid=pw->pw_uid; + } + + /* Move to the next bit */ + thisline=ptr+1; + if (thisline>=loaded_list_end) + ohshit("unexpected end of line in statusoverride file"); + + /* Extract the gid */ + if (!(ptr=memchr(thisline, ' ', nextline-thisline))) + ohshit("syntax error in statusoverride file "); + *ptr=0; + if (thisline[0]=='#') { + fso->gid=strtol(thisline + 1, &endptr, 10); + if (*endptr!=0) + ohshit("syntax error: invalid gid in statusoverride file "); + } else { + struct group* gr = getgrnam(thisline); + if (gr==NULL) + ohshit("syntax error: unknown group `%s' in statusoverride file ", thisline); + fso->gid=gr->gr_gid; + } + + /* Move to the next bit */ + thisline=ptr+1; + if (thisline>=loaded_list_end) + ohshit("unexecpted end of line in statusoverride file"); + + /* Extract the mode */ + if (!(ptr=memchr(thisline, ' ', nextline-thisline))) + ohshit("syntax error in statusoverride file "); + *ptr=0; + fso->mode=strtol(thisline, &endptr, 8); + if (*endptr!=0) + ohshit("syntax error: invalid mode in statusoverride file "); + + /* Move to the next bit */ + thisline=ptr+1; + if (thisline>=loaded_list_end) + ohshit("unexecpted end of line in statusoverride file"); + + fnn= findnamenode(thisline, 0); + if (fnn->statoverride) + ohshit("multiple statusoverides present for file `%.250s'", thisline); + fnn->statoverride=fso; + /* Moving on.. */ + thisline=nextline; + } + + onerr_abort--; +} + +void ensure_diversions(void) { + static struct varbuf vb; + + struct stat stab1, stab2; + char linebuf[MAXDIVERTFILENAME]; + FILE *file; + struct diversion *ov, *oicontest, *oialtname; + int l; + + varbufreset(&vb); + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" DIVERSIONSFILE); + varbufaddc(&vb,0); + + onerr_abort++; + + file= fopen(vb.buf,"r"); + if (!file) { + if (errno != ENOENT) ohshite(_("failed to open diversions file")); + if (!diversionsfile) { onerr_abort--; return; } + } else if (diversionsfile) { + if (fstat(fileno(diversionsfile),&stab1)) + ohshite(_("failed to fstat previous diversions file")); + if (fstat(fileno(file),&stab2)) + ohshite(_("failed to fstat diversions file")); + if (stab1.st_dev == stab2.st_dev && stab1.st_ino == stab2.st_ino) { + fclose(file); onerr_abort--; return; + } + } + if (diversionsfile) fclose(diversionsfile); + diversionsfile= file; + setcloexec(fileno(diversionsfile), vb.buf); + + for (ov= diversions; ov; ov= ov->next) { + ov->useinstead->divert->camefrom->divert= 0; + ov->useinstead->divert= 0; + } + diversions= 0; + if (!file) { onerr_abort--; return; } + + while (fgets(linebuf,sizeof(linebuf),file)) { + oicontest= nfmalloc(sizeof(struct diversion)); + oialtname= nfmalloc(sizeof(struct diversion)); + + l= strlen(linebuf); + if (l == 0) ohshit(_("fgets gave an empty string from diversions [i]")); + if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [i]")); + linebuf[l]= 0; + oialtname->camefrom= findnamenode(linebuf, 0); + oialtname->useinstead= 0; + + if (!fgets(linebuf,sizeof(linebuf),file)) { + if (ferror(file)) ohshite(_("read error in diversions [ii]")); + else ohshit(_("unexpected EOF in diversions [ii]")); + } + l= strlen(linebuf); + if (l == 0) ohshit(_("fgets gave an empty string from diversions [ii]")); + if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [ii]")); + linebuf[l]= 0; + oicontest->useinstead= findnamenode(linebuf, 0); + oicontest->camefrom= 0; + + if (!fgets(linebuf,sizeof(linebuf),file)) { + if (ferror(file)) ohshite(_("read error in diversions [iii]")); + else ohshit(_("unexpected EOF in diversions [iii]")); + } + l= strlen(linebuf); + if (l == 0) ohshit(_("fgets gave an empty string from diversions [iii]")); + if (linebuf[--l] != '\n') ohshit(_("diversions file has too-long line or EOF [ii]")); + linebuf[l]= 0; + + oicontest->pkg= oialtname->pkg= + strcmp(linebuf,":") ? findpackage(linebuf) : 0; + + if (oialtname->camefrom->divert || oicontest->useinstead->divert) + ohshit(_("conflicting diversions involving `%.250s' or `%.250s'"), + oialtname->camefrom->name, oicontest->useinstead->name); + + oialtname->camefrom->divert= oicontest; + oicontest->useinstead->divert= oialtname; + + oicontest->next= diversions; + diversions= oicontest; + } + if (ferror(file)) ohshite(_("read error in diversions [i]")); + + onerr_abort--; +} + +struct fileiterator { + struct filenamenode *namenode; + int nbinn; +}; + +#define BINS (1 << 17) + /* This must always be a power of two. If you change it + * consider changing the per-character hashing factor (currently + * 1785 = 137*13) too. + */ + +static struct filenamenode *bins[BINS]; + +struct fileiterator *iterfilestart(void) { + struct fileiterator *i; + i= m_malloc(sizeof(struct fileiterator)); + i->namenode= 0; + i->nbinn= 0; + return i; +} + +struct filenamenode *iterfilenext(struct fileiterator *i) { + struct filenamenode *r= NULL; + + while (!i->namenode) { + if (i->nbinn >= BINS) return 0; + i->namenode= bins[i->nbinn++]; + } + r= i->namenode; + i->namenode= r->next; + return r; +} + +void iterfileend(struct fileiterator *i) { + free(i); +} + +void filesdbinit(void) { + struct filenamenode *fnn; + int i; + + for (i=0; i<BINS; i++) + for (fnn= bins[i]; fnn; fnn= fnn->next) { + fnn->flags= 0; + fnn->oldhash= 0; + fnn->filestat= 0; + } +} + +static int hash(const char *name) { + int v= 0; + while (*name) { v *= 1787; v += *name; name++; } + return v; +} + +struct filenamenode *findnamenode(const char *name, enum fnnflags flags) { + struct filenamenode **pointerp, *newnode; + const char *orig_name = name; + + /* We skip initial slashes and ./ pairs, and add our own single leading slash. */ + name= skip_slash_dotslash(name); + + pointerp= bins + (hash(name) & (BINS-1)); + while (*pointerp) { +/* Why is this assert nescessary? It is checking already added entries. */ + assert((*pointerp)->name[0] == '/'); + if (!strcmp((*pointerp)->name+1,name)) break; + pointerp= &(*pointerp)->next; + } + if (*pointerp) return *pointerp; + + newnode= nfmalloc(sizeof(struct filenamenode)); + newnode->packages= 0; + if((flags & fnn_nocopy) && name > orig_name && name[-1] == '/') + newnode->name = name - 1; + else { + char *newname= nfmalloc(strlen(name)+2); + newname[0]= '/'; strcpy(newname+1,name); + newnode->name= newname; + } + newnode->flags= 0; + newnode->next= 0; + newnode->divert= 0; + newnode->statoverride= 0; + newnode->filestat= 0; + *pointerp= newnode; + nfiles++; + + return newnode; +} + +/* vi: ts=8 sw=2 + */ diff --git a/src/filesdb.h b/src/filesdb.h new file mode 100644 index 000000000..cedaafeaf --- /dev/null +++ b/src/filesdb.h @@ -0,0 +1,148 @@ +/* + * dpkg - main program for package management + * filesdb.h - management of database of files installed on system + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef FILESDB_H +#define FILESDB_H + +/* + * Data structure here is as follows: + * + * For each package we have a `struct fileinlist *', the head of + * a list of files in that package. They are in `forwards' order. + * Each entry has a pointer to the `struct filenamenode'. + * + * The struct filenamenodes are in a hash table, indexed by name. + * (This hash table is not visible to callers.) + * + * Each filenamenode has a (possibly empty) list of `struct + * filepackage', giving a list of the packages listing that + * filename. + * + * When we read files contained info about a particular package + * we set the `files' member of the clientdata struct to the + * appropriate thing. When not yet set the files pointer is + * made to point to `fileslist_uninited' (this is available only + * internally, withing filesdb.c - the published interface is + * ensure_*_available). + */ + +struct pkginfo; + +/* flags to findnamenode() */ + +enum fnnflags { + fnn_nocopy= 000001, /* do not need to copy filename */ +}; + +struct filenamenode { + struct filenamenode *next; + const char *name; + struct filepackages *packages; + struct diversion *divert; + struct filestatoverride *statoverride; + /* Fields from here on are used by archives.c &c, and cleared by + * filesdbinit. + */ + enum { + fnnf_new_conff= 000001, /* in the newconffiles list */ + fnnf_new_inarchive= 000002, /* in the new filesystem archive */ + fnnf_old_conff= 000004, /* in the old package's conffiles list */ + fnnf_elide_other_lists= 000010, /* must remove from other packages' lists */ + fnnf_no_atomic_overwrite= 000020, /* >=1 instance is a dir, cannot rename over */ + } flags; /* Set to zero when a new node is created. */ + const char *oldhash; /* valid iff this namenode is in the newconffiles list */ + struct stat *filestat; +}; + +struct fileinlist { + struct fileinlist *next; + struct filenamenode *namenode; +}; + +struct filestatoverride { + /* We allow the administrator to override the owner, group and mode of + * a file. If such an override is present we use that instead of the + * stat information stored in the archive. + * + * This functionality used to be in the suidmanager package. + */ + uid_t uid; + gid_t gid; + mode_t mode; +}; + +struct diversion { + /* When we deal with an `overridden' file, every package except + * the overriding one is considered to contain the other file + * instead. Both files have entries in the filesdb database, and + * they refer to each other via these diversion structures. + * + * The contested filename's filenamenode has an diversion entry + * with useinstead set to point to the redirected filename's + * filenamenode; the redirected filenamenode has camefrom set to the + * contested filenamenode. Both sides' diversion entries will + * have pkg set to the package (if any) which is allowed to use the + * contended filename. + * + * Packages that contain either version of the file will all + * refer to the contested filenamenode in their per-file package lists + * (both in core and on disk). References are redirected to the other + * filenamenode's filename where appropriate. + */ + struct filenamenode *useinstead; + struct filenamenode *camefrom; + struct pkginfo *pkg; + struct diversion *next; + /* The `contested' halves are in this list for easy cleanup. */ +}; + +#define PERFILEPACKAGESLUMP 10 + +struct filepackages { + struct filepackages *more; + struct pkginfo *pkgs[PERFILEPACKAGESLUMP]; + /* pkgs is a null-pointer-terminated list; anything after the first null + * is garbage + */ +}; + +struct fileiterator; +struct fileiterator *iterfilestart(void); +struct filenamenode *iterfilenext(struct fileiterator *i); +void iterfileend(struct fileiterator *i); + +void ensure_diversions(void); +void ensure_statoverrides(void); + +void ensure_packagefiles_available(struct pkginfo *pkg); +void ensure_allinstfiles_available(void); +void ensure_allinstfiles_available_quiet(void); +void note_must_reread_files_inpackage(struct pkginfo *pkg); +struct filenamenode *findnamenode(const char *filename, enum fnnflags flags); +void write_filelist_except(struct pkginfo *pkg, struct fileinlist *list, int leaveout); + +struct reversefilelistiter { struct fileinlist *todo; }; + +void reversefilelist_init(struct reversefilelistiter *iterptr, struct fileinlist *files); +struct filenamenode *reversefilelist_next(struct reversefilelistiter *iterptr); +void reversefilelist_abort(struct reversefilelistiter *iterptr); + +#endif /* FILESDB_H */ diff --git a/src/help.c b/src/help.c new file mode 100644 index 000000000..8a4746208 --- /dev/null +++ b/src/help.c @@ -0,0 +1,480 @@ +/* + * dpkg - main program for package management + * help.c - various helper routines + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <errno.h> +#include <unistd.h> +#include <dirent.h> +#include <assert.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <signal.h> + +#include <dpkg.h> +#include <dpkg-db.h> + +#include "filesdb.h" +#include "main.h" + +const char *const statusstrings[]= { + N_("not installed"), + N_("unpacked but not configured"), + N_("broken due to postinst failure"), + N_("installed"), + N_("broken due to failed removal"), + N_("not installed but configs remain") +}; + +struct filenamenode *namenodetouse(struct filenamenode *namenode, struct pkginfo *pkg) { + struct filenamenode *r; + + if (!namenode->divert) return namenode; + + debug(dbg_eachfile,"namenodetouse namenode=`%s' pkg=%s", + namenode->name,pkg->name); + + r= + (namenode->divert->useinstead && namenode->divert->pkg != pkg) + ? namenode->divert->useinstead : namenode; + + debug(dbg_eachfile, + "namenodetouse ... useinstead=%s camefrom=%s pkg=%s return %s", + namenode->divert->useinstead ? namenode->divert->useinstead->name : "<none>", + namenode->divert->camefrom ? namenode->divert->camefrom->name : "<none>", + namenode->divert->pkg ? namenode->divert->pkg->name : "<none>", + r->name); + return r; +} + +void checkpath(void) { +/* Verify that some programs can be found in the PATH. */ + static const char *const checklist[]= { "ldconfig", +#ifdef WITH_START_STOP_DAEMON + "start-stop-daemon", +#endif + "install-info", "update-rc.d", 0 + }; + + struct stat stab; + const char *const *clp; + const char *path, *s, *p; + char* buf; + int warned= 0; + long l; + + path= getenv("PATH"); + if (!path) ohshit(_("dpkg - error: PATH is not set.\n")); + buf=(char*)m_malloc(strlen(path)+2+strlen("start-stop-daemon")); + + for (clp=checklist; *clp; clp++) { + s= path; + while (s) { + p= strchr(s,':'); + l= p ? p-s : (long)strlen(s); + memcpy(buf,s,l); + if (l) buf[l++]= '/'; + strcpy(buf+l,*clp); + if (stat(buf,&stab) == 0 && (stab.st_mode & 0111)) break; + s= p; if (s) s++; + } + if (!s) { + fprintf(stderr,_("dpkg: `%s' not found on PATH.\n"),*clp); + warned++; + } + } + + free(buf); + if (warned) + forcibleerr(fc_badpath,_("%d expected program(s) not found on PATH.\nNB: root's " + "PATH should usually contain /usr/local/sbin, /usr/sbin and /sbin."), + warned); +} + +void ensure_package_clientdata(struct pkginfo *pkg) { + if (pkg->clientdata) return; + pkg->clientdata= nfmalloc(sizeof(struct perpackagestate)); + pkg->clientdata->istobe= itb_normal; + pkg->clientdata->fileslistvalid= 0; + pkg->clientdata->files= 0; +} + +void cu_closepipe(int argc, void **argv) { + int *p1= (int*)argv[0]; + close(p1[0]); close(p1[1]); +} + +void cu_closefile(int argc, void **argv) { + FILE *f= (FILE*)(argv[0]); + fclose(f); +} + +void cu_closedir(int argc, void **argv) { + DIR *d= (DIR*)(argv[0]); + closedir(d); +} + +void cu_closefd(int argc, void **argv) { + int ip= *(int*)argv[0]; + close(ip); +} + +int ignore_depends(struct pkginfo *pkg) { + struct packageinlist *id; + for (id= ignoredependss; id; id= id->next) + if (id->pkg == pkg) return 1; + return 0; +} + +int force_depends(struct deppossi *possi) { + return fc_depends || + ignore_depends(possi->ed) || + ignore_depends(possi->up->up); +} + +int force_conflicts(struct deppossi *possi) { + return fc_conflicts; +} + +const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile) { + static struct varbuf vb; + varbufreset(&vb); + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" INFODIR); + varbufaddstr(&vb,pkg->name); + varbufaddc(&vb,'.'); + varbufaddstr(&vb,whichfile); + varbufaddc(&vb,0); + return vb.buf; +} + +static const char* preexecscript(const char *path, char *const *argv) { + /* returns the path to the script inside the chroot + * none of the stuff here will work if admindir isn't inside instdir + * as expected. - fixme + */ + size_t instdirl; + + if (*instdir) { + if (chroot(instdir)) ohshite(_("failed to chroot to `%.250s'"),instdir); + } + if (f_debug & dbg_scripts) { + fprintf(stderr,"D0%05o: fork/exec %s (",dbg_scripts,path); + while (*argv) fprintf(stderr," %s",*argv++); + fputs(" )\n",stderr); + } + instdirl= strlen(instdir); + if (!instdirl) return path; + assert (strlen(path)>=instdirl); + return path+instdirl; +} + +static char *const *vbuildarglist(const char *scriptname, va_list ap) { + static char *bufs[PKGSCRIPTMAXARGS+1]; + char *nextarg; + int i; + + i=0; + if(bufs[0]) free(bufs[0]); + bufs[i++]= strdup(scriptname); /* yes, cast away const because exec wants it that way */ + for (;;) { + assert(i < PKGSCRIPTMAXARGS); + nextarg= va_arg(ap,char*); + if (!nextarg) break; + bufs[i++]= nextarg; + } + bufs[i]= 0; + return bufs; +} + +static char *const *buildarglist(const char *scriptname, ...) { + char *const *arglist; + va_list ap; + va_start(ap,scriptname); + arglist= vbuildarglist(scriptname,ap); + va_end(ap); + return arglist; +} + +#define NSCRIPTCATCHSIGNALS (int)(sizeof(script_catchsignallist)/sizeof(int)-1) +static int script_catchsignallist[]= { SIGQUIT, SIGINT, 0 }; +static struct sigaction script_uncatchsignal[NSCRIPTCATCHSIGNALS]; + +static void cu_restorescriptsignals(int argc, void **argv) { + int i; + for (i=0; i<NSCRIPTCATCHSIGNALS; i++) { + if (sigaction(script_catchsignallist[i],&script_uncatchsignal[i],0)) { + fprintf(stderr,_("error un-catching signal %s: %s\n"), + strsignal(script_catchsignallist[i]),strerror(errno)); + onerr_abort++; + } + } +} + +static void script_catchsignals(void) { + int i; + struct sigaction catchsig; + + onerr_abort++; + memset(&catchsig,0,sizeof(catchsig)); + catchsig.sa_handler= SIG_IGN; + sigemptyset(&catchsig.sa_mask); + catchsig.sa_flags= 0; + for (i=0; i<NSCRIPTCATCHSIGNALS; i++) + if (sigaction(script_catchsignallist[i],&catchsig,&script_uncatchsignal[i])) + ohshite(_("unable to ignore signal %s before running script"), + strsignal(script_catchsignallist[i])); + push_cleanup(cu_restorescriptsignals,~0, 0,0, 0); + onerr_abort--; +} + +static void setexecute(const char *path, struct stat *stab) { + if ((stab->st_mode & 0555) == 0555) return; + if (!chmod(path,0755)) return; + ohshite(_("unable to set execute permissions on `%.250s'"),path); +} +static int do_script(const char *pkg, const char *scriptname, const char *scriptpath, struct stat *stab, char *const arglist[], const char *desc, const char *name, int warn) { + const char *scriptexec; + int c1, r; + setexecute(scriptpath,stab); + + c1= m_fork(); + if (!c1) { + const char **narglist; + for (r=0; arglist[r]; r++) ; + narglist=nfmalloc((r+1)*sizeof(char*)); + for (r=1; arglist[r-1]; r++) + narglist[r]= arglist[r]; + scriptexec= preexecscript(scriptpath,(char * const *)narglist); + narglist[0]= scriptexec; + execv(scriptexec,(char * const *)narglist); + ohshite(desc,name); + } + script_catchsignals(); /* This does a push_cleanup() */ + r= waitsubproc(c1,name,warn); + pop_cleanup(ehflag_normaltidy); + return r; +} + +int maintainer_script_installed(struct pkginfo *pkg, const char *scriptname, + const char *description, ...) { + /* all ...'s are const char*'s */ + const char *scriptpath; + char *const *arglist; + struct stat stab; + va_list ap; + char buf[100]; + + scriptpath= pkgadminfile(pkg,scriptname); + va_start(ap,description); + arglist= vbuildarglist(scriptname,ap); + va_end(ap); + sprintf(buf,"%s script",description); + + if (stat(scriptpath,&stab)) { + if (errno == ENOENT) { + debug(dbg_scripts,"maintainer_script_installed nonexistent %s",scriptname); + return 0; + } + ohshite(_("unable to stat installed %s script `%.250s'"),description,scriptpath); + } + do_script(pkg->name, scriptname, scriptpath, &stab, arglist, _("unable to execute %s"), buf, 0); + ensure_diversions(); + return 1; +} + +int maintainer_script_new(const char *pkgname, + const char *scriptname, const char *description, + const char *cidir, char *cidirrest, ...) { + char *const *arglist; + struct stat stab; + va_list ap; + char buf[100]; + + va_start(ap,cidirrest); + arglist= vbuildarglist(scriptname,ap); + va_end(ap); + sprintf(buf,"%s script",description); + + strcpy(cidirrest,scriptname); + if (stat(cidir,&stab)) { + if (errno == ENOENT) { + debug(dbg_scripts,"maintainer_script_new nonexistent %s `%s'",scriptname,cidir); + return 0; + } + ohshite(_("unable to stat new %s script `%.250s'"),description,cidir); + } + do_script(pkgname, scriptname, cidir, &stab, arglist, _("unable to execute new %s"), buf, 0); + ensure_diversions(); + return 1; +} + +int maintainer_script_alternative(struct pkginfo *pkg, + const char *scriptname, const char *description, + const char *cidir, char *cidirrest, + const char *ifok, const char *iffallback) { + const char *oldscriptpath; + char *const *arglist; + struct stat stab; + char buf[100]; + + oldscriptpath= pkgadminfile(pkg,scriptname); + arglist= buildarglist(scriptname, + ifok,versiondescribe(&pkg->available.version, + vdew_nonambig), + (char*)0); + sprintf(buf,_("old %s script"),description); + if (stat(oldscriptpath,&stab)) { + if (errno == ENOENT) { + debug(dbg_scripts,"maintainer_script_alternative nonexistent %s `%s'", + scriptname,oldscriptpath); + return 0; + } + fprintf(stderr, + _("dpkg: warning - unable to stat %s `%.250s': %s\n"), + buf,oldscriptpath,strerror(errno)); + } else { + if (!do_script(pkg->name, scriptname, oldscriptpath, &stab, arglist, _("unable to execute %s"), buf, PROCWARN)) + return 1; + ensure_diversions(); + } + fprintf(stderr, _("dpkg - trying script from the new package instead ...\n")); + + arglist= buildarglist(scriptname, + iffallback,versiondescribe(&pkg->installed.version, + vdew_nonambig), + (char*)0); + strcpy(cidirrest,scriptname); + sprintf(buf,_("new %s script"),description); + + if (stat(cidir,&stab)) { + if (errno == ENOENT) + ohshit(_("there is no script in the new version of the package - giving up")); + else + ohshite(_("unable to stat %s `%.250s'"),buf,cidir); + } + + do_script(pkg->name, scriptname, cidir, &stab, arglist, _("unable to execute %s"), buf, 0); + fprintf(stderr, _("dpkg: ... it looks like that went OK.\n")); + + ensure_diversions(); + return 1; +} + +void clear_istobes(void) { + struct pkgiterator *it; + struct pkginfo *pkg; + + it= iterpkgstart(); + while ((pkg= iterpkgnext(it)) != 0) { + ensure_package_clientdata(pkg); + pkg->clientdata->istobe= itb_normal; + pkg->clientdata->replacingfilesandsaid= 0; + } +} + +void debug(int which, const char *fmt, ...) { + va_list ap; + if (!(f_debug & which)) return; + fprintf(stderr,"D0%05o: ",which); + va_start(ap,fmt); + vfprintf(stderr,fmt,ap); + va_end(ap); + putc('\n',stderr); +} + +int isdirectoryinuse(struct filenamenode *file, struct pkginfo *pkg) { + /* Returns 1 if the file is used by packages other than pkg, 0 otherwise. */ + struct filepackages *packageslump; + int i; + + debug(dbg_veryverbose, "isdirectoryinuse `%s' (except %s)", file->name, + pkg ? pkg->name : "<none>"); + for (packageslump= file->packages; packageslump; packageslump= packageslump->more) { + debug(dbg_veryverbose, "isdirectoryinuse packageslump %s ...", + packageslump->pkgs[0] ? packageslump->pkgs[0]->name : "<none>"); + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + debug(dbg_veryverbose, "isdirectoryinuse considering [%d] %s ...", i, + packageslump->pkgs[i]->name); + if (packageslump->pkgs[i] == pkg) continue; + return 1; + } + } + debug(dbg_veryverbose, "isdirectoryinuse no"); + return 0; +} + +void oldconffsetflags(const struct conffile *searchconff) { + struct filenamenode *namenode; + + while (searchconff) { + namenode= findnamenode(searchconff->name, 0); /* XXX */ + namenode->flags |= fnnf_old_conff; + debug(dbg_conffdetail, "oldconffsetflags `%s' namenode %p flags %o", + searchconff->name, namenode, namenode->flags); + searchconff= searchconff->next; + } +} + +int chmodsafe_unlink(const char *pathname) { + struct stat stab; + + if (lstat(pathname,&stab)) return -1; + if (S_ISREG(stab.st_mode) ? (stab.st_mode & 07000) : + !(S_ISLNK(stab.st_mode) || S_ISDIR(stab.st_mode) || + S_ISFIFO(stab.st_mode) || S_ISSOCK(stab.st_mode))) { + /* We chmod it if it is 1. a sticky or set-id file, or 2. an unrecognised + * object (ie, not a file, link, directory, fifo or socket + */ + if (chmod(pathname,0600)) return -1; + } + if (unlink(pathname)) return -1; + return 0; +} + +void ensure_pathname_nonexisting(const char *pathname) { + int c1; + const char *u; + + u= skip_slash_dotslash(pathname); + assert(*u); + + debug(dbg_eachfile,"ensure_pathname_nonexisting `%s'",pathname); + if (!rmdir(pathname)) return; /* Deleted it OK, it was a directory. */ + if (errno == ENOENT || errno == ELOOP) return; + if (errno == ENOTDIR) { + /* Either it's a file, or one of the path components is. If one + * of the path components is this will fail again ... + */ + if (!chmodsafe_unlink(pathname)) return; /* OK, it was */ + if (errno == ENOTDIR) return; + } + if (errno != ENOTEMPTY) /* Huh ? */ + ohshite(_("failed to rmdir/unlink `%.255s'"),pathname); + c1= m_fork(); + if (!c1) { + execlp(RM,"rm","-rf","--",pathname,(char*)0); + ohshite(_("failed to exec rm for cleanup")); + } + debug(dbg_eachfile,"ensure_pathname_nonexisting running rm -rf"); + waitsubproc(c1,"rm cleanup",0); +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 000000000..955fc4b89 --- /dev/null +++ b/src/main.c @@ -0,0 +1,563 @@ +/* + * dpkg - main program for package management + * main.c - main program + * + * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <ctype.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <myopt.h> + +#include "main.h" + +static void printversion(void) { + if (fputs(_("Debian GNU/Linux `"), stdout) < 0) werr("stdout"); + if (fputs(DPKG, stdout) < 0) werr("stdout"); + if (fputs(_("' package management program version "), stdout) < 0) werr("stdout"); + if (fputs( DPKG_VERSION_ARCH ".\n", stdout) < 0) werr("stdout"); + if (fputs(_( "This is free software; see the GNU General Public Licence version 2 or\n" + "later for copying conditions. There is NO warranty.\n" + "See " DPKG " --licence for copyright and license details.\n"), + stdout) < 0) werr("stdout"); +} +/* + options that need fixing: + dpkg --yet-to-unpack \n\ + */ +static void usage(void) { + if (fprintf (stdout, _("\ +Usage: \n\ + dpkg -i|--install <.deb file name> ... | -R|--recursive <dir> ...\n\ + dpkg --unpack <.deb file name> ... | -R|--recursive <dir> ...\n\ + dpkg -A|--record-avail <.deb file name> ... | -R|--recursive <dir> ...\n\ + dpkg --configure <package name> ... | -a|--pending\n\ + dpkg -r|--remove | -P|--purge <package name> ... | -a|--pending\n\ + dpkg --get-selections [<pattern> ...] get list of selections to stdout\n\ + dpkg --set-selections set package selections from stdin\n\ + dpkg --update-avail <Packages-file> replace available packages info\n\ + dpkg --merge-avail <Packages-file> merge with info from file\n\ + dpkg --clear-avail erase existing available info\n\ + dpkg --forget-old-unavail forget uninstalled unavailable pkgs\n\ + dpkg -s|--status <package-name> ... display package status details\n\ + dpkg -p|--print-avail <package-name> ... display available version details\n\ + dpkg -L|--listfiles <package-name> ... list files `owned' by package(s)\n\ + dpkg -l|--list [<pattern> ...] list packages concisely\n\ + dpkg -S|--search <pattern> ... find package(s) owning file(s)\n\ + dpkg -C|--audit check for broken package(s)\n\ + dpkg --print-architecture print dpkg architecture\n\ + dpkg --compare-versions <a> <rel> <b> compare version numbers - see below\n\ + dpkg --help | --version show this help / version number\n\ + dpkg --force-help | -Dh|--debug=help help on forcing resp. debugging\n\ + dpkg --licence print copyright licensing terms\n\ +\n\ +Use dpkg -b|--build|-c|--contents|-e|--control|-I|--info|-f|--field|\n\ + -x|--extract|-X|--vextract|--fsys-tarfile on archives (type %s --help.)\n\ +\n\ +For internal use: dpkg --assert-support-predepends | --predep-package |\n\ + --assert-working-epoch | --assert-long-filenames | --assert-multi-conrep\n\ +\n\ +Options:\n\ + --admindir=<directory> Use <directory> instead of %s\n\ + --root=<directory> Install on alternative system rooted elsewhere\n\ + --instdir=<directory> Change inst'n root without changing admin dir\n\ + -O|--selected-only Skip packages not selected for install/upgrade\n\ + -E|--skip-same-version Skip packages whose same version is installed\n\ + -G|--refuse-downgrade Skip packages with earlier version than installed\n\ + -B|--auto-deconfigure Install even if it would break some other package\n\ + --no-debsig Do no try to verify package signatures\n\ + --no-act|--dry-run|--simulate\n\ + Just say what we would do - don't do it\n\ + -D|--debug=<octal> Enable debugging - see -Dhelp or --debug=help\n\ + --status-fd <n> Send status change updates to file descriptor <n>\n\ + --ignore-depends=<package>,... Ignore dependencies involving <package>\n\ + --force-... Override problems - see --force-help\n\ + --no-force-...|--refuse-... Stop when problems encountered\n\ + --abort-after <n> Abort after encountering <n> errors\n\ +\n\ +Comparison operators for --compare-versions are:\n\ + lt le eq ne ge gt (treat empty version as earlier than any version);\n\ + lt-nl le-nl ge-nl gt-nl (treat empty version as later than any version);\n\ + < << <= = >= >> > (only for compatibility with control file syntax).\n\ +\n\ +Use `dselect' or 'aptitude' for user-friendly package management.\n"), + BACKEND, ADMINDIR) < 0) werr ("stdout"); +} + +const char thisname[]= "dpkg"; +const char architecture[]= ARCHITECTURE; +const char printforhelp[]= N_("\ +Type dpkg --help for help about installing and deinstalling packages [*];\n\ +Use dselect for user-friendly package management;\n\ +Type dpkg -Dhelp for a list of dpkg debug flag values;\n\ +Type dpkg --force-help for a list of forcing options;\n\ +Type dpkg-deb --help for help about manipulating *.deb files;\n\ +Type dpkg --licence for copyright licence and lack of warranty (GNU GPL) [*].\n\ +\n\ +Options marked [*] produce a lot of output - pipe it through `less' or `more' !"); + +const struct cmdinfo *cipaction= 0; +int f_pending=0, f_recursive=0, f_alsoselect=1, f_skipsame=0, f_noact=0; +int f_autodeconf=0, f_nodebsig=0; +unsigned long f_debug=0; +/* Change fc_overwrite to 1 to enable force-overwrite by default */ +int fc_downgrade=1, fc_configureany=0, fc_hold=0, fc_removereinstreq=0, fc_overwrite=0; +int fc_removeessential=0, fc_conflicts=0, fc_depends=0, fc_dependsversion=0; +int fc_autoselect=1, fc_badpath=0, fc_overwritediverted=0, fc_architecture=0; +int fc_nonroot=0, fc_overwritedir=0, fc_conff_new=0, fc_conff_miss=0; +int fc_conff_old=0, fc_conff_def=0; +int fc_badverify = 0; + +int errabort = 50; +const char *admindir= ADMINDIR; +const char *instdir= ""; +struct packageinlist *ignoredependss=0; + +static const struct forceinfo { + const char *name; + int *opt; +} forceinfos[]= { + { "downgrade", &fc_downgrade }, + { "configure-any", &fc_configureany }, + { "hold", &fc_hold }, + { "remove-reinstreq", &fc_removereinstreq }, + { "remove-essential", &fc_removeessential }, + { "conflicts", &fc_conflicts }, + { "confnew", &fc_conff_new }, + { "confold", &fc_conff_old }, + { "confdef", &fc_conff_def }, + { "confmiss", &fc_conff_miss }, + { "depends", &fc_depends }, + { "depends-version", &fc_dependsversion }, + { "auto-select", &fc_autoselect }, + { "bad-path", &fc_badpath }, + { "not-root", &fc_nonroot }, + { "overwrite", &fc_overwrite }, + { "overwrite-diverted", &fc_overwritediverted }, + { "overwrite-dir", &fc_overwritedir }, + { "architecture", &fc_architecture }, + { "bad-verify", &fc_badverify }, + { 0 } +}; + +static void helponly(const struct cmdinfo *cip, const char *value) NONRETURNING; +static void helponly(const struct cmdinfo *cip, const char *value) { + usage(); exit(0); +} +static void versiononly(const struct cmdinfo *cip, const char *value) NONRETURNING; +static void versiononly(const struct cmdinfo *cip, const char *value) { + printversion(); exit(0); +} + +static void setaction(const struct cmdinfo *cip, const char *value) { + if (cipaction) + badusage(_("conflicting actions --%s and --%s"),cip->olong,cipaction->olong); + cipaction= cip; +} + +static void setobsolete(const struct cmdinfo *cip, const char *value) { + fprintf(stderr, _("Warning: obsolete option `--%s'\n"),cip->olong); +} + +static void setdebug(const struct cmdinfo *cpi, const char *value) { + char *endp; + + if (*value == 'h') { + if (printf( +_("%s debugging option, --debug=<octal> or -D<octal>:\n\n\ + number ref. in source description\n\ + 1 general Generally helpful progress information\n\ + 2 scripts Invocation and status of maintainer scripts\n\ + 10 eachfile Output for each file processed\n\ + 100 eachfiledetail Lots of output for each file processed\n\ + 20 conff Output for each configuration file\n\ + 200 conffdetail Lots of output for each configuration file\n\ + 40 depcon Dependencies and conflicts\n\ + 400 depcondetail Lots of dependencies/conflicts output\n\ + 1000 veryverbose Lots of drivel about eg the dpkg/info directory\n\ + 2000 stupidlyverbose Insane amounts of drivel\n\n\ +Debugging options are be mixed using bitwise-or.\n\ +Note that the meanings and values are subject to change.\n"), + DPKG) < 0) werr("stdout"); + exit(0); + } + + f_debug= strtoul(value,&endp,8); + if (*endp) badusage(_("--debug requires an octal argument")); +} + +static void setroot(const struct cmdinfo *cip, const char *value) { + char *p; + instdir= value; + p= m_malloc(strlen(value) + sizeof(ADMINDIR)); + strcpy(p,value); + strcat(p,ADMINDIR); + admindir= p; +} + +static void ignoredepends(const struct cmdinfo *cip, const char *value) { + char *copy, *p; + const char *pnerr; + struct packageinlist *ni; + + copy= m_malloc(strlen(value)+2); + strcpy(copy,value); + copy[strlen(value)+1]= 0; + for (p=copy; *p; p++) { + if (*p != ',') continue; + *p++= 0; + if (!*p || *p==',' || p==copy+1) + badusage(_("null package name in --ignore-depends comma-separated list `%.250s'"), + value); + } + p= copy; + while (*p) { + pnerr= illegal_packagename(value,0); + if (pnerr) ohshite(_("--ignore-depends requires a legal package name. " + "`%.250s' is not; %s"), value, pnerr); + ni= m_malloc(sizeof(struct packageinlist)); + ni->pkg= findpackage(value); + ni->next= ignoredependss; + ignoredependss= ni; + p+= strlen(p)+1; + } +} + +static void setinteger(const struct cmdinfo *cip, const char *value) { + unsigned long v; + char *ep; + + v= strtoul(value,&ep,0); + if (*ep || v > INT_MAX) + badusage(_("invalid integer for --%s: `%.250s'"),cip->olong,value); + *cip->iassignto= v; +} + +static void setpipe(const struct cmdinfo *cip, const char *value) { + static struct pipef **lastpipe; + unsigned long v; + char *ep; + + v= strtoul(value,&ep,0); + if (*ep || v > INT_MAX) + badusage(_("invalid integer for --%s: `%.250s'"),cip->olong,value); + + lastpipe= cip->parg; + if (*lastpipe) { + (*lastpipe)->next= nfmalloc(sizeof(struct pipef)); + *lastpipe= (*lastpipe)->next; + } else { + *lastpipe= nfmalloc(sizeof(struct pipef)); + } + (*lastpipe)->fd= v; + (*lastpipe)->next= NULL; +} + +static void setforce(const struct cmdinfo *cip, const char *value) { + const char *comma; + size_t l; + const struct forceinfo *fip; + + if (!strcmp(value,"help")) { + if (printf(_("%s forcing options - control behaviour when problems found:\n\ + warn but continue: --force-<thing>,<thing>,...\n\ + stop with error: --refuse-<thing>,<thing>,... | --no-force-<thing>,...\n\ + Forcing things:\n\ + all Set all force options\n\ + auto-select [*] (De)select packages to install (remove) them\n\ + downgrade [*] Replace a package with a lower version\n\ + configure-any Configure any package which may help this one\n\ + hold Process incidental packages even when on hold\n\ + bad-path PATH is missing important programs, problems likely\n\ + not-root Try to (de)install things even when not root\n\ + overwrite Overwrite a file from one package with another\n\ + overwrite-diverted Overwrite a diverted file with an undiverted version\n\ + bad-verify Install a package even if it fails authenticity check\n\ + depends-version [!] Turn dependency version problems into warnings\n\ + depends [!] Turn all dependency problems into warnings\n\ + confnew [!] Always use the new config files, don't prompt\n\ + confold [!] Always use the old config files, don't prompt\n\ + confdef [!] Use the default option for new config files if one\n\ + is available, don't prompt. If no default can be found,\n\ + you will be prompted unless one of the confold or\n\ + confnew options is also given\n\ + confmiss [!] Always install missing config files\n\ + conflicts [!] Allow installation of conflicting packages\n\ + architecture [!] Process even packages with wrong architecture\n\ + overwrite-dir [!] Overwrite one package's directory with another's file\n\ + remove-reinstreq [!] Remove packages which require installation\n\ + remove-essential [!] Remove an essential package\n\ +\n\ +WARNING - use of options marked [!] can seriously damage your installation.\n\ +Forcing options marked [*] are enabled by default.\n"), + DPKG) < 0) werr("stdout"); + exit(0); + } + + for (;;) { + comma= strchr(value,','); + l= comma ? (int)(comma-value) : strlen(value); + for (fip=forceinfos; fip->name; fip++) + if (!strncmp(fip->name,value,l) && strlen(fip->name)==l) break; + if (!fip->name) + if(!strncmp("all",value,l)) + for (fip=forceinfos; fip->name; fip++) + *fip->opt= cip->arg; + else + badusage(_("unknown force/refuse option `%.*s'"), l<250 ? (int)l : 250, value); + else + *fip->opt= cip->arg; + if (!comma) break; + value= ++comma; + } +} + +static const char okpassshortopts[]= "D"; + +void execbackend(const char *const *argv) NONRETURNING; +void commandfd(const char *const *argv); +static const struct cmdinfo cmdinfos[]= { + /* This table has both the action entries in it and the normal options. + * The action entries are made with the ACTION macro, as they all + * have a very similar structure. + */ +#define ACTION(longopt,shortopt,code,function) \ + { longopt, shortopt, 0,0,0, setaction, code, 0, (voidfnp)function } +#define OBSOLETE(longopt,shortopt) \ + { longopt, shortopt, 0,0,0, setobsolete, 0, 0, 0 } +#define ACTIONBACKEND(longopt,shortop, backend) \ + { longopt, shortop, 0,0,0, setaction, 0, (void*)backend, (voidfnp)execbackend } + + ACTION( "install", 'i', act_install, archivefiles ), + ACTION( "unpack", 0, act_unpack, archivefiles ), + ACTION( "record-avail", 'A', act_avail, archivefiles ), + ACTION( "configure", 0, act_configure, packages ), + ACTION( "remove", 'r', act_remove, packages ), + ACTION( "purge", 'P', act_purge, packages ), + ACTIONBACKEND( "listfiles", 'L', DPKGQUERY), + ACTIONBACKEND( "status", 's', DPKGQUERY), + ACTION( "get-selections", 0, act_getselections, getselections ), + ACTION( "set-selections", 0, act_setselections, setselections ), + ACTIONBACKEND( "print-avail", 'p', DPKGQUERY), + ACTION( "update-avail", 0, act_avreplace, updateavailable ), + ACTION( "merge-avail", 0, act_avmerge, updateavailable ), + ACTION( "clear-avail", 0, act_avclear, updateavailable ), + ACTION( "forget-old-unavail", 0, act_forgetold, forgetold ), + ACTION( "audit", 'C', act_audit, audit ), + ACTION( "yet-to-unpack", 0, act_unpackchk, unpackchk ), + ACTIONBACKEND( "list", 'l', DPKGQUERY), + ACTIONBACKEND( "search", 'S', DPKGQUERY), + ACTION( "assert-support-predepends", 0, act_assertpredep, assertpredep ), + ACTION( "assert-working-epoch", 0, act_assertepoch, assertepoch ), + ACTION( "assert-long-filenames", 0, act_assertlongfilenames, assertlongfilenames ), + ACTION( "assert-multi-conrep", 0, act_assertmulticonrep, assertmulticonrep ), + ACTION( "print-architecture", 0, act_printarch, printarch ), + ACTION( "print-installation-architecture", 0, act_printinstarch, printarch ), + ACTION( "predep-package", 0, act_predeppackage, predeppackage ), + ACTION( "compare-versions", 0, act_cmpversions, cmpversions ), +/* + ACTION( "command-fd", 'c', act_commandfd, commandfd ), +*/ + + { "status-fd", 0, 1, 0, 0, setpipe, 0, &status_pipes }, + { "pending", 'a', 0, &f_pending, 0, 0, 1 }, + { "recursive", 'R', 0, &f_recursive, 0, 0, 1 }, + { "no-act", 0, 0, &f_noact, 0, 0, 1 }, + { "dry-run", 0, 0, &f_noact, 0, 0, 1 }, + { "simulate", 0, 0, &f_noact, 0, 0, 1 }, + { "no-debsig", 0, 0, &f_nodebsig, 0, 0, 1 }, + { 0, 'G', 0, &fc_downgrade, 0, 0, /* alias for --refuse */ 0 }, + { "selected-only", 'O', 0, &f_alsoselect, 0, 0, 0 }, + { "no-also-select", 'N', 0, &f_alsoselect, 0,0,0 /* fixme: remove sometime */ }, + { "skip-same-version", 'E', 0, &f_skipsame, 0, 0, 1 }, + { "auto-deconfigure", 'B', 0, &f_autodeconf, 0, 0, 1 }, + OBSOLETE( "largemem", 0 ), + OBSOLETE( "smallmem", 0 ), + { "root", 0, 1, 0, 0, setroot }, + { "abort-after", 0, 1, &errabort, 0, setinteger, 0 }, + { "admindir", 0, 1, 0, &admindir, 0 }, + { "instdir", 0, 1, 0, &instdir, 0 }, + { "ignore-depends", 0, 1, 0, 0, ignoredepends }, + { "force", 0, 2, 0, 0, setforce, 1 }, + { "refuse", 0, 2, 0, 0, setforce, 0 }, + { "no-force", 0, 2, 0, 0, setforce, 0 }, + { "debug", 'D', 1, 0, 0, setdebug }, + { "help", 'h', 0, 0, 0, helponly }, + { "version", 0, 0, 0, 0, versiononly }, + { "licence",/* UK spelling */ 0,0,0,0, showcopyright }, + { "license",/* US spelling */ 0,0,0,0, showcopyright }, + ACTIONBACKEND( "build", 'b', BACKEND), + ACTIONBACKEND( "contents", 'c', BACKEND), + ACTIONBACKEND( "control", 'e', BACKEND), + ACTIONBACKEND( "info", 'I', BACKEND), + ACTIONBACKEND( "field", 'f', BACKEND), + ACTIONBACKEND( "extract", 'x', BACKEND), + ACTIONBACKEND( "new", 0, BACKEND), + ACTIONBACKEND( "old", 0, BACKEND), + ACTIONBACKEND( "vextract", 'X', BACKEND), + ACTIONBACKEND( "fsys-tarfile", 0, BACKEND), + { 0, 0 } +}; + +void execbackend(const char *const *argv) { + char **nargv; + int i, argc = 1; + const char *const *arg = argv; + while(*arg != 0) { arg++; argc++; } + nargv= malloc(sizeof(char *) * (argc + 2)); + + if (!nargv) ohshite(_("couldn't malloc in execbackend")); + nargv[0]= strdup(cipaction->parg); + if (!nargv[0]) ohshite(_("couldn't strdup in execbackend")); + nargv[1]= malloc(strlen(cipaction->olong) + 3); + if (!nargv[1]) ohshite(_("couldn't malloc in execbackend")); + strcpy(nargv[1], "--"); + strcat(nargv[1], cipaction->olong); + for (i= 2; i <= argc; i++) { + nargv[i]= strdup(argv[i-2]); + if (!nargv[i]) ohshite(_("couldn't strdup in execbackend")); + } + nargv[i]= 0; + execvp(cipaction->parg, nargv); + ohshite(_("failed to exec %s"),(char *)cipaction->parg); +} +void commandfd(const char *const *argv) { + jmp_buf ejbuf; + struct varbuf linevb; + const char * pipein; + const char **newargs; + char *ptr, *endptr; + FILE *in; + int c, lno, infd, i, skipchar; + static void (*actionfunction)(const char *const *argv); + + if ((pipein= *argv++) == NULL) badusage(_("--command-fd takes 1 argument, not 0")); + if (*argv) badusage(_("--command-fd only takes 1 argument")); + if ((infd= strtol(pipein, (char **)NULL, 10)) == -1) + ohshite(_("invalid number for --command-fd")); + if ((in= fdopen(infd, "r")) == NULL) + ohshite(_("couldn't open `%i' for stream"), infd); + + if (setjmp(ejbuf)) { /* expect warning about possible clobbering of argv */ + error_unwind(ehflag_bombout); exit(2); + } + varbufinit(&linevb); + for (;;lno= 0) { + const char **oldargs= NULL; + int argc= 1, mode= 0; + lno= 0; + push_error_handler(&ejbuf,print_error_fatal,0); + + do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && isspace(c)); + if (c == EOF) break; + if (c == '#') { + do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && c != '\n'); + continue; + } + varbufreset(&linevb); + do { + varbufaddc(&linevb,c); + c= getc(in); + if (c == '\n') lno++; + if (isspace(c)) argc++; /* This isn't fully accurate, but overestimating can't hurt. */ + } while (c != EOF && c != '\n'); + if (c == EOF) ohshit(_("unexpected eof before end of line %d"),lno); + if (!argc) continue; + varbufaddc(&linevb,0); +printf("line=`%*s'\n",(int)linevb.used,linevb.buf); + oldargs= newargs= realloc(oldargs,sizeof(const char *) * (argc + 1)); + argc= 1; + ptr= linevb.buf; + endptr= ptr + linevb.used; + skipchar= 0; + while(ptr < endptr) { + if (skipchar) { + skipchar= 0; + } else if (*ptr == '\\') { + memmove(ptr, (ptr+1), (linevb.used-(linevb.buf - ptr)-1)); + endptr--; + skipchar= 1; + continue; + } else if (isspace(*ptr)) { + if (mode == 1) { + *ptr= 0; + mode= 0; + } + } else { + if (mode == 0) { + newargs[argc]= ptr; + argc++; + mode= 1; + } + } + ptr++; + } + *ptr= 0; + newargs[argc++] = 0; + +/* We strdup each argument, but never free it, because the error messages + * contain references back to these strings. Freeing them, and reusing + * the memory, would make those error messages confusing, to say the + * least. + */ + for(i=1;i<argc;i++) + if (newargs[i]) newargs[i]=strdup(newargs[i]); + + cipaction= NULL; + myopt((const char *const**)&newargs,cmdinfos); + if (!cipaction) badusage(_("need an action option")); + + actionfunction= (void (*)(const char* const*))cipaction->farg; + actionfunction(newargs); + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + } +} + + +int main(int argc, const char *const *argv) { + jmp_buf ejbuf; + static void (*actionfunction)(const char *const *argv); + + standard_startup(&ejbuf, argc, &argv, DPKG, 1, cmdinfos); + if (!cipaction) badusage(_("need an action option")); + + setvbuf(stdout,0,_IONBF,0); + filesdbinit(); + + actionfunction= (void (*)(const char* const*))cipaction->farg; + + actionfunction(argv); + + standard_shutdown(0); + + return reportbroken_retexitstatus(); +} diff --git a/src/main.h b/src/main.h new file mode 100644 index 000000000..7c6e33e06 --- /dev/null +++ b/src/main.h @@ -0,0 +1,223 @@ +/* + * dpkg - main program for package management + * main.h - external definitions for this program + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef MAIN_H +#define MAIN_H + +struct fileinlist; /* these two are defined in filesdb.h */ +struct filenamenode; + +struct perpackagestate { + enum istobes { + itb_normal, itb_remove, itb_installnew, itb_deconfigure, itb_preinstall + } istobe; + + /* filelistvalid files meaning + * 0 0 not read yet, must do so if want them + * 0 !=0 read, but rewritten and now out of + * date. If want info must throw away old + * and reread file. + * 1 !=0 read, all is OK + * 1 0 read OK, but, there were no files + */ + int fileslistvalid; + struct fileinlist *files; + int replacingfilesandsaid; +}; + +struct packageinlist { + struct packageinlist *next; + struct pkginfo *pkg; +}; + +enum action { act_unset, act_install, act_unpack, act_avail, act_configure, + act_remove, act_purge, act_listpackages, act_avreplace, act_avmerge, + act_unpackchk, act_status, act_searchfiles, act_audit, act_listfiles, + act_assertpredep, act_printarch, act_predeppackage, act_cmpversions, + act_printinstarch, act_compareversions, act_printavail, act_avclear, + act_forgetold, act_getselections, act_setselections, + act_assertepoch, act_assertlongfilenames, act_assertmulticonrep, + act_commandfd }; + +enum conffopt { + cfof_prompt = 001, + cfof_keep = 002, + cfof_install = 004, + cfof_backup = 0100, + cfof_newconff = 0200, + cfof_isnew = 0400, + cfom_main = 007, + cfo_keep = cfof_keep, + cfo_prompt_keep = cfof_keep | cfof_prompt, + cfo_prompt = cfof_prompt, + cfo_prompt_install = cfof_prompt | cfof_install, + cfo_install = cfof_install, + cfo_newconff = cfof_install | cfof_newconff, + cfo_identical = cfof_keep +}; + +extern int conffoptcells[2][2]; +extern const char *const statusstrings[]; + +extern const struct cmdinfo *cipaction; +extern int f_pending, f_recursive, f_alsoselect, f_skipsame, f_noact; +extern int f_autodeconf, f_largemem, f_nodebsig; +extern unsigned long f_debug; +extern int fc_downgrade, fc_configureany, fc_hold, fc_removereinstreq, fc_overwrite; +extern int fc_removeessential, fc_conflicts, fc_depends, fc_dependsversion; +extern int fc_autoselect, fc_badpath, fc_overwritediverted, fc_architecture; +extern int fc_nonroot, fc_overwritedir, fc_conff_new, fc_conff_miss; +extern int fc_conff_old, fc_conff_def; +extern int fc_badverify; + +extern int errabort; +extern const char *admindir; +extern const char *instdir; +extern struct packageinlist *ignoredependss; +extern const char architecture[]; + +/* from filesdb.c */ + +void filesdbinit(void); + +/* from archives.c */ + +void archivefiles(const char *const *argv); +void process_archive(const char *filename); +int wanttoinstall(struct pkginfo *pkg, const struct versionrevision *ver, int saywhy); + +/* from update.c */ + +void forgetold(const char *const *argv); +void updateavailable(const char *const *argv); + +/* from enquiry.c */ + +void listpackages(const char *const *argv); +void audit(const char *const *argv); +void unpackchk(const char *const *argv); +void showpackages(const char *const *argv); +void searchfiles(const char *const *argv); +void enqperpackage(const char *const *argv); +void assertepoch(const char *const *argv); +void assertpredep(const char *const *argv); +void assertlongfilenames(const char *const *argv); +void assertmulticonrep(const char *const *argv); +void predeppackage(const char *const *argv); +void printarch(const char *const *argv); +void printinstarch(const char *const *argv); +void cmpversions(const char *const *argv) NONRETURNING; + +/* Intended for use as a comparison function for qsort when + * sorting an array of pointers to struct pkginfo: + */ +int pkglistqsortcmp(const void *a, const void *b); + +/* from select.c */ + +void getselections(const char *const *argv); +void setselections(const char *const *argv); + +/* from packages.c, remove.c and configure.c */ + +void add_to_queue(struct pkginfo *pkg); +void process_queue(void); +void packages(const char *const *argv); +void removal_bulk(struct pkginfo *pkg); +int conffderef(struct pkginfo *pkg, struct varbuf *result, const char *in); +int dependencies_ok(struct pkginfo *pkg, struct pkginfo *removing, + struct varbuf *aemsgs); + +void deferred_remove(struct pkginfo *pkg); +void deferred_configure(struct pkginfo *pkg); + +extern int queuelen, sincenothing, dependtry; + +/* from cleanup.c (most of these are declared in archives.h) */ + +void cu_prermremove(int argc, void **argv); + +/* from errors.c */ + +extern int nerrs; +void print_error_perpackage(const char *emsg, const char *arg); +void forcibleerr(int forceflag, const char *format, ...) PRINTFFORMAT(2,3); +int reportbroken_retexitstatus(void); +int skip_due_to_hold(struct pkginfo *pkg); + +/* from help.c */ + +void cu_closefile(int argc, void **argv); +void cu_closepipe(int argc, void **argv); +void cu_closedir(int argc, void **argv); +void cu_closefd(int argc, void **argv); + +int ignore_depends(struct pkginfo *pkg); +int force_depends(struct deppossi *possi); +int force_conff_new(struct deppossi *possi); +int force_conff_miss(struct deppossi *possi); +int force_conflicts(struct deppossi *possi); +void ensure_package_clientdata(struct pkginfo *pkg); +const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile); +void oldconffsetflags(const struct conffile *searchconff); +void ensure_pathname_nonexisting(const char *pathname); +int chmodsafe_unlink(const char *pathname); /* chmod 600, then unlink */ +void checkpath(void); +struct filenamenode *namenodetouse(struct filenamenode*, struct pkginfo*); + +/* all ...'s are const char*'s ... */ +int maintainer_script_installed(struct pkginfo *pkg, const char *scriptname, + const char *description, ...); +int maintainer_script_new(const char *pkgname, + const char *scriptname, const char *description, + const char *cidir, char *cidirrest, ...); +int maintainer_script_alternative(struct pkginfo *pkg, + const char *scriptname, const char *description, + const char *cidir, char *cidirrest, + const char *ifok, const char *iffallback); +void clear_istobes(void); +int isdirectoryinuse(struct filenamenode *namenode, struct pkginfo *pkg); + +enum debugflags { + dbg_general= 00001, + dbg_scripts= 00002, + dbg_eachfile= 00010, + dbg_eachfiledetail= 00100, + dbg_conff= 00020, + dbg_conffdetail= 00200, + dbg_depcon= 00040, + dbg_depcondetail= 00400, + dbg_veryverbose= 01000, + dbg_stupidlyverbose= 02000, +}; + +void debug(int which, const char *fmt, ...) PRINTFFORMAT(2,3); +void check_libver(void); + +/* from depcon.c */ + +int depisok(struct dependency *dep, struct varbuf *whynot, + struct pkginfo **fixbyrm, int allowunconfigd); +struct cyclesofarlink; +int findbreakcycle(struct pkginfo *pkg); +void describedepcon(struct varbuf *addto, struct dependency *dep); + +#endif /* MAIN_H */ diff --git a/src/packages.c b/src/packages.c new file mode 100644 index 000000000..1c81b1884 --- /dev/null +++ b/src/packages.c @@ -0,0 +1,417 @@ +/* + * dpkg - main program for package management + * packages.c - common to actions that process packages + * + * Copyright (C) 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <ctype.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <myopt.h> + +#include "filesdb.h" +#include "main.h" + +struct pkginqueue { + struct pkginqueue *next; + struct pkginfo *pkg; +}; + +static struct pkginqueue *queuehead= 0, **queuetail= &queuehead; + +int queuelen=0, sincenothing=0, dependtry=0; + +void add_to_queue(struct pkginfo *pkg) { + struct pkginqueue *newent; + + newent= m_malloc(sizeof(struct pkginqueue)); + newent->pkg= pkg; + newent->next= 0; + *queuetail= newent; + queuetail= &newent->next; + + queuelen++; +} + +void packages(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + const char *thisarg; + size_t l; + + modstatdb_init(admindir, + f_noact ? msdbrw_readonly + : fc_nonroot ? msdbrw_write + : msdbrw_needsuperuser); + checkpath(); + + if (f_pending) { + + if (*argv) + badusage(_("--%s --pending does not take any non-option arguments"),cipaction->olong); + + it= iterpkgstart(); + while ((pkg= iterpkgnext(it)) != 0) { + switch (cipaction->arg) { + case act_configure: + if (pkg->status != stat_unpacked && pkg->status != stat_halfconfigured) + continue; + if (pkg->want != want_install) + continue; + break; + case act_remove: + case act_purge: + if (pkg->want != want_purge) { + if (pkg->want != want_deinstall) continue; + if (pkg->status == stat_configfiles) continue; + } + if (pkg->status == stat_notinstalled) + continue; + break; + default: + internerr("unknown action for pending"); + } + add_to_queue(pkg); + } + iterpkgend(it); + + } else { + + if (!*argv) + badusage(_("--%s needs at least one package name argument"), cipaction->olong); + + while ((thisarg= *argv++) != 0) { + pkg= findpackage(thisarg); + if (pkg->status == stat_notinstalled) { + l= strlen(pkg->name); + if (l >= sizeof(DEBEXT) && !strcmp(pkg->name+l-sizeof(DEBEXT)+1,DEBEXT)) + badusage(_("you must specify packages by their own names," + " not by quoting the names of the files they come in")); + } + add_to_queue(pkg); + } + + } + + ensure_diversions(); + + process_queue(); + + modstatdb_shutdown(); +} + +void process_queue(void) { + struct pkginqueue *removeent, *rundown; + struct pkginfo *volatile pkg; + jmp_buf ejbuf; + enum istobes istobe= itb_normal; + + clear_istobes(); + + switch (cipaction->arg) { + case act_configure: case act_install: istobe= itb_installnew; break; + case act_remove: case act_purge: istobe= itb_remove; break; + default: internerr("unknown action for queue start"); + } + for (rundown= queuehead; rundown; rundown= rundown->next) { + ensure_package_clientdata(rundown->pkg); + if (rundown->pkg->clientdata->istobe == istobe) { + /* Remove it from the queue - this is a second copy ! */ + switch (cipaction->arg) { + case act_configure: case act_remove: case act_purge: + printf(_("Package %s listed more than once, only processing once.\n"), + rundown->pkg->name); + break; + case act_install: + printf(_("More than one copy of package %s has been unpacked\n" + " in this run ! Only configuring it once.\n"), + rundown->pkg->name); + break; + default: + internerr("unknown action in duplicate"); + } + rundown->pkg= 0; + } else { + rundown->pkg->clientdata->istobe= istobe; + } + } + + while (queuelen) { + removeent= queuehead; + assert(removeent); + queuehead= queuehead->next; + queuelen--; + if (queuetail == &removeent->next) queuetail= &queuehead; + + pkg= removeent->pkg; + free(removeent); + + if (!pkg) continue; /* duplicate, which we removed earlier */ + + assert(pkg->status <= stat_configfiles); + + if (setjmp(ejbuf)) { + /* give up on it from the point of view of other packages, ie reset istobe */ + pkg->clientdata->istobe= itb_normal; + error_unwind(ehflag_bombout); + if (onerr_abort > 0) break; + continue; + } + push_error_handler(&ejbuf,print_error_perpackage,pkg->name); + if (sincenothing++ > queuelen*2+2) { + dependtry++; sincenothing= 0; + assert(dependtry <= 4); + } + switch (cipaction->arg) { + case act_install: + /* Don't try to configure pkgs that we've just disappeared. */ + if (pkg->status == stat_notinstalled) + break; + case act_configure: + deferred_configure(pkg); + break; + case act_remove: case act_purge: + deferred_remove(pkg); + break; + default: + internerr("unknown action in queue"); + } + if (ferror(stdout)) werr("stdout"); + if (ferror(stderr)) werr("stderr"); + set_error_display(0,0); + error_unwind(ehflag_normaltidy); + } +} + +/*** dependency processing - common to --configure and --remove ***/ + +/* + * The algorithm for deciding what to configure or remove first is as + * follows: + * + * Loop through all packages doing a `try 1' until we've been round and + * nothing has been done, then do `try 2' and `try 3' likewise. + * + * When configuring, in each try we check to see whether all + * dependencies of this package are done. If so we do it. If some of + * the dependencies aren't done yet but will be later we defer the + * package, otherwise it is an error. + * + * When removing, in each try we check to see whether there are any + * packages that would have dependencies missing if we removed this + * one. If not we remove it now. If some of these packages are + * themselves scheduled for removal we defer the package until they + * have been done. + * + * The criteria for satisfying a dependency vary with the various + * tries. In try 1 we treat the dependencies as absolute. In try 2 we + * check break any cycles in the dependency graph involving the package + * we are trying to process before trying to process the package + * normally. In try 3 (which should only be reached if + * --force-depends-version is set) we ignore version number clauses in + * Depends lines. In try 4 (only reached if --force-depends is set) we + * say "ok" regardless. + * + * If we are configuring and one of the packages we depend on is + * awaiting configuration but wasn't specified in the argument list we + * will add it to the argument list if --configure-any is specified. + * In this case we note this as having "done something" so that we + * don't needlessly escalate to higher levels of dependency checking + * and breaking. + */ + +static int deppossi_ok_found(struct pkginfo *possdependee, + struct pkginfo *requiredby, + struct pkginfo *removing, + struct pkginfo *providing, + int *matched, + struct deppossi *checkversion, + int *interestingwarnings, + struct varbuf *oemsgs) { + int thisf; + + if (ignore_depends(possdependee)) { + debug(dbg_depcondetail," ignoring depended package so ok and found"); + return 3; + } + thisf= 0; + if (possdependee == removing) { + varbufaddstr(oemsgs,_(" Package ")); + varbufaddstr(oemsgs,possdependee->name); + if (providing) { + varbufaddstr(oemsgs,_(" which provides ")); + varbufaddstr(oemsgs,providing->name); + } + varbufaddstr(oemsgs,_(" is to be removed.\n")); + *matched= 1; + if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1; + debug(dbg_depcondetail," removing possdependee, returning %d",thisf); + return thisf; + } + switch (possdependee->status) { + case stat_installed: + case stat_unpacked: + case stat_halfconfigured: + assert(possdependee->installed.valid); + if (checkversion && !versionsatisfied(&possdependee->installed,checkversion)) { + varbufaddstr(oemsgs,_(" Version of ")); + varbufaddstr(oemsgs,possdependee->name); + varbufaddstr(oemsgs,_(" on system is ")); + varbufaddstr(oemsgs,versiondescribe(&possdependee->installed.version, + vdew_nonambig)); + varbufaddstr(oemsgs,".\n"); + assert(checkversion->verrel != dvr_none); + if (fc_depends) thisf= (dependtry >= 3) ? 2 : 1; + debug(dbg_depcondetail," bad version, returning %d",thisf); + (*interestingwarnings)++; + return thisf; + } + if (possdependee->status == stat_installed) { + debug(dbg_depcondetail," is installed, ok and found"); + return 3; + } + if (possdependee->clientdata && + possdependee->clientdata->istobe == itb_installnew) { + debug(dbg_depcondetail," unpacked/halfconfigured, defer"); + return 1; + } else if (!removing && fc_configureany && !skip_due_to_hold(possdependee)) { + fprintf(stderr, + _("dpkg: also configuring `%s' (required by `%s')\n"), + possdependee->name, requiredby->name); + add_to_queue(possdependee); sincenothing=0; return 1; + } else { + varbufaddstr(oemsgs,_(" Package ")); + varbufaddstr(oemsgs,possdependee->name); + if (providing) { + varbufaddstr(oemsgs,_(" which provides ")); + varbufaddstr(oemsgs,providing->name); + } + varbufaddstr(oemsgs,_(" is not configured yet.\n")); + if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1; + debug(dbg_depcondetail," not configured/able - returning %d",thisf); + (*interestingwarnings)++; + return thisf; + } + default: + varbufaddstr(oemsgs,_(" Package ")); + varbufaddstr(oemsgs,possdependee->name); + if (providing) { + varbufaddstr(oemsgs,_(" which provides ")); + varbufaddstr(oemsgs,providing->name); + } + varbufaddstr(oemsgs,_(" is not installed.\n")); + if (fc_depends) thisf= (dependtry >= 4) ? 2 : 1; + debug(dbg_depcondetail," not installed - returning %d",thisf); + (*interestingwarnings)++; + return thisf; + } +} + +int dependencies_ok(struct pkginfo *pkg, struct pkginfo *removing, + struct varbuf *aemsgs) { + int ok, matched, found, thisf, interestingwarnings; + struct varbuf oemsgs; + struct dependency *dep; + struct deppossi *possi, *provider; + + varbufinit(&oemsgs); + interestingwarnings= 0; + ok= 2; /* 2=ok, 1=defer, 0=halt */ + debug(dbg_depcon,"checking dependencies of %s (- %s)", + pkg->name, removing ? removing->name : "<none>"); + assert(pkg->installed.valid); + for (dep= pkg->installed.depends; dep; dep= dep->next) { + if (dep->type != dep_depends && dep->type != dep_predepends) continue; + debug(dbg_depcondetail," checking group ..."); + matched= 0; varbufreset(&oemsgs); + found= 0; /* 0=none, 1=defer, 2=withwarning, 3=ok */ + for (possi= dep->list; found != 3 && possi; possi= possi->next) { + debug(dbg_depcondetail," checking possibility -> %s",possi->ed->name); + if (possi->cyclebreak) { + debug(dbg_depcondetail," break cycle so ok and found"); + found= 3; break; + } + thisf= deppossi_ok_found(possi->ed,pkg,removing,0, + &matched,possi,&interestingwarnings,&oemsgs); + if (thisf > found) found= thisf; + if (found != 3 && possi->verrel == dvr_none) { + if (possi->ed->installed.valid) { + for (provider= possi->ed->installed.depended; + found != 3 && provider; + provider= provider->nextrev) { + if (provider->up->type != dep_provides) continue; + debug(dbg_depcondetail," checking provider %s",provider->up->up->name); + thisf= deppossi_ok_found(provider->up->up,pkg,removing,possi->ed, + &matched,0,&interestingwarnings,&oemsgs); + if (thisf > found) found= thisf; + } + } + } + debug(dbg_depcondetail," found %d",found); + if (thisf > found) found= thisf; + } + debug(dbg_depcondetail," found %d matched %d",found,matched); + if (removing && !matched) continue; + switch (found) { + case 0: + ok= 0; + case 2: + varbufaddstr(aemsgs, " "); + varbufaddstr(aemsgs, pkg->name); + varbufaddstr(aemsgs, _(" depends on ")); + varbufdependency(aemsgs, dep); + if (interestingwarnings) { + /* Don't print the line about the package to be removed if + * that's the only line. + */ + varbufaddstr(aemsgs, _("; however:\n")); + varbufaddc(&oemsgs, 0); + varbufaddstr(aemsgs, oemsgs.buf); + } else { + varbufaddstr(aemsgs, ".\n"); + } + break; + case 1: + if (ok>1) ok= 1; + break; + case 3: + break; + default: + internerr("unknown value for found"); + } + } + if (ok == 0 && (pkg->clientdata && pkg->clientdata->istobe == itb_remove)) + ok= 1; + + varbuffree(&oemsgs); + debug(dbg_depcon,"ok %d msgs >>%.*s<<", ok, (int)aemsgs->used, aemsgs->buf); + return ok; +} diff --git a/src/processarc.c b/src/processarc.c new file mode 100644 index 000000000..9de7e9dee --- /dev/null +++ b/src/processarc.c @@ -0,0 +1,1055 @@ +/* + * dpkg - main program for package management + * processarc.c - the huge function process_archive + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> +#include <assert.h> +#include <time.h> +#include <ctype.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <tarfn.h> +#include <myopt.h> + +#include "filesdb.h" +#include "main.h" +#include "archives.h" + +void process_archive(const char *filename) { + static const struct TarFunctions tf = { + tarfileread, + tarobject, tarobject, tarobject, tarobject, tarobject + }; + + /* These need to be static so that we can pass their addresses to + * push_cleanup as arguments to the cu_xxx routines; if an error occurs + * we unwind the stack before processing the cleanup list, and these + * variables had better still exist ... + */ + static int p1[2]; + static char cidirtmpnambuf[L_tmpnam+100]; + static char *cidirbuf=0, *reasmbuf=0; + static struct fileinlist *newconffiles, *newfileslist; + static enum pkgstatus oldversionstatus; + static struct varbuf infofnvb, fnvb, depprobwhy; + static struct tarcontext tc; + + int c1, r, admindirlen, i, infodirlen, infodirbaseused, status; + struct pkgiterator *it; + struct pkginfo *pkg, *otherpkg, *divpkg; + char *cidir, *cidirrest, *p; + char *pfilenamebuf, conffilenamebuf[MAXCONFFILENAME]; + const char *pfilename, *newinfofilename; + struct fileinlist *newconff, **newconffileslastp; + struct fileinlist *cfile; + struct reversefilelistiter rlistit; + struct conffile *searchconff, **iconffileslastp, *newiconff; + struct filepackages *packageslump; + struct dependency *dsearch, *newdeplist, **newdeplistlastp; + struct dependency *newdep, *dep, *providecheck; + struct deppossi *psearch, **newpossilastp, *possi, *newpossi, *pdep; + FILE *conff; + DIR *dsd; + struct filenamenode *namenode; + struct dirent *de; + struct stat stab; + struct packageinlist *deconpil, *deconpiltemp; + + cleanup_pkg_failed= cleanup_conflictor_failed= 0; + admindirlen= strlen(admindir); + + for (pfilename= filename ; pfilename && strlen(pfilename) > 30 && + strchr(pfilename,'/') != NULL ; pfilename++ ) + pfilename= strchr(pfilename,'/'); + if (pfilename && pfilename != filename) { + pfilenamebuf= (char *)nfmalloc(strlen(pfilename)+5); + strcpy(pfilenamebuf,".../"); + strcat(pfilenamebuf,pfilename); + pfilename= pfilenamebuf; + } else { + pfilename= filename; + } + + if (stat(filename,&stab)) ohshite(_("cannot access archive")); + + if (!f_noact) { + /* We can't `tentatively-reassemble' packages. */ + if (!reasmbuf) { + reasmbuf= m_malloc(admindirlen+sizeof(REASSEMBLETMP)+5); + strcpy(reasmbuf,admindir); + strcat(reasmbuf,"/" REASSEMBLETMP); + } + if (unlink(reasmbuf) && errno != ENOENT) + ohshite(_("error ensuring `%.250s' doesn't exist"),reasmbuf); + push_cleanup(cu_pathname,~0, 0,0, 1,(void*)reasmbuf); + c1= m_fork(); + if (!c1) { + execlp(SPLITTER, SPLITTER,"-Qao",reasmbuf,filename,(char*)0); + ohshite(_("failed to exec dpkg-split to see if it's part of a multiparter")); + } + while ((r= waitpid(c1,&status,0)) == -1 && errno == EINTR); + if (r != c1) { onerr_abort++; ohshite(_("wait for dpkg-split failed")); } + switch (WIFEXITED(status) ? WEXITSTATUS(status) : -1) { + case 0: + /* It was a part - is it complete ? */ + if (!stat(reasmbuf,&stab)) { /* Yes. */ + filename= reasmbuf; + pfilename= _("reassembled package file"); + break; + } else if (errno == ENOENT) { /* No. That's it, we skip it. */ + return; + } + case 1: + /* No, it wasn't a part. */ + break; + default: + checksubprocerr(status,SPLITTER,0); + } + } + + /* Verify the package. */ + if (!f_nodebsig && (stat(DEBSIGVERIFY, &stab)==0)) { + printf(_("Authenticating %s ...\n"), filename); + fflush(stdout); + c1 = m_fork(); + if (!c1) { + execl(DEBSIGVERIFY, DEBSIGVERIFY, "-q", filename, NULL); + ohshite(_("failed to execl debsig-verify")); + } else { + int status; + waitpid(c1, &status, 0); + if (!(WIFEXITED(status) && WEXITSTATUS(status) == 0)) { + if (! fc_badverify) { + ohshit(_("Verification on package %s failed!"), filename); + } else { + fprintf(stderr, _("Verification on package %s failed,\nbut installing anyway as you request.\n"), filename); + } + } else { + printf(_("passed\n")); + } + } + } + + + if (f_noact) { + cidir= cidirtmpnambuf; + /* We use tmpnam here, not to get a unique filename, but to get a unique directory. */ + if (!tmpnam(cidir)) ohshite(_("unable to get unique filename for control info")); + strcat(cidir,"/"); + } else { + /* We want it to be on the same filesystem so that we can + * use rename(2) to install the postinst &c. + */ + if (!cidirbuf) + cidirbuf= m_malloc(admindirlen+sizeof(CONTROLDIRTMP)+MAXCONTROLFILENAME+10); + cidir= cidirbuf; + strcpy(cidir,admindir); + strcat(cidir, "/" CONTROLDIRTMP); + } + cidirrest= cidir + strlen(cidir); + + assert(*cidir && cidirrest[-1] == '/'); cidirrest[-1]= 0; + ensure_pathname_nonexisting(cidir); cidirrest[-1]= '/'; + + push_cleanup(cu_cidir,~0, 0,0, 2,(void*)cidir,(void*)cidirrest); + c1= m_fork(); + if (!c1) { + cidirrest[-1]= 0; + execlp(BACKEND, BACKEND,"--control",filename,cidir,(char*)0); + ohshite(_("failed to exec dpkg-deb to extract control information")); + } + waitsubproc(c1,BACKEND " --control",0); + strcpy(cidirrest,CONTROLFILE); + + parsedb(cidir, pdb_recordavailable|pdb_rejectstatus|pdb_ignorefiles|pdb_weakclassification, + &pkg,NULL,NULL); + if (!pkg->files) { + pkg->files= nfmalloc(sizeof(struct filedetails)); + pkg->files->next= 0; + pkg->files->name= pkg->files->msdosname= pkg->files->md5sum= 0; + } + /* Always nfmalloc. Otherwise, we may overwrite some other field(like md5sum). */ + pkg->files->size= nfmalloc(30); + sprintf(pkg->files->size,"%lu",(unsigned long)stab.st_size); + + if (cipaction->arg == act_avail) { + printf(_("Recorded info about %s from %s.\n"),pkg->name,pfilename); + pop_cleanup(ehflag_normaltidy); + return; + } + + if (pkg->available.architecture && *pkg->available.architecture && + strcmp(pkg->available.architecture,"all") && + strcmp(pkg->available.architecture,architecture)) + forcibleerr(fc_architecture, + _("package architecture (%s) does not match system (%s)"), + pkg->available.architecture,architecture); + + if (!pkg->installed.valid) blankpackageperfile(&pkg->installed); + assert(pkg->available.valid); + + for (deconpil= deconfigure; + deconpil; + deconpil= deconpiltemp) { + deconpiltemp= deconpil->next; + free(deconpil); + } + deconfigure= 0; + clear_istobes(); + + if (!wanttoinstall(pkg,&pkg->available.version,1)) { + pop_cleanup(ehflag_normaltidy); + return; + } + + /* Check if anything is installed that we conflict with, or not installed + * that we need */ + pkg->clientdata->istobe= itb_installnew; + + for (dsearch= pkg->available.depends; dsearch; dsearch= dsearch->next) { + switch (dsearch->type) { + case dep_conflicts: + /* Look for things we conflict with. */ + check_conflict(dsearch, pkg, pfilename); + break; + case dep_provides: + /* Look for things that conflict with what we provide. */ + if (dsearch->list->ed->installed.valid) { + for (psearch= dsearch->list->ed->installed.depended; + psearch; + psearch= psearch->nextrev) { + if (psearch->up->type != dep_conflicts) continue; + check_conflict(psearch->up, pkg, pfilename); + } + } + break; + case dep_suggests: + case dep_recommends: + case dep_depends: + case dep_replaces: + case dep_enhances: + /* Ignore these here. */ + break; + case dep_predepends: + if (!depisok(dsearch,&depprobwhy,0,1)) { + varbufaddc(&depprobwhy,0); + fprintf(stderr, _("dpkg: regarding %s containing %s, pre-dependency problem:\n%s"), + pfilename, pkg->name, depprobwhy.buf); + if (!force_depends(dsearch->list)) + ohshit(_("pre-dependency problem - not installing %.250s"),pkg->name); + fprintf(stderr, _("dpkg: warning - ignoring pre-dependency problem !\n")); + } + } + } + /* Look for things that conflict with us. */ + for (psearch= pkg->installed.depended; psearch; psearch= psearch->nextrev) { + if (psearch->up->type != dep_conflicts) continue; + check_conflict(psearch->up, pkg, pfilename); + } + + ensure_allinstfiles_available(); + filesdbinit(); + + if (pkg->status != stat_notinstalled && pkg->status != stat_configfiles) + printf(_("Preparing to replace %s %s (using %s) ...\n"), + pkg->name, + versiondescribe(&pkg->installed.version,vdew_nonambig), + pfilename); + else + printf(_("Unpacking %s (from %s) ...\n"),pkg->name,pfilename); + + if (f_noact) { + pop_cleanup(ehflag_normaltidy); + return; + } + + /* OK, we're going ahead. First we read the conffiles, and copy the + * hashes across. + */ + newconffiles= 0; newconffileslastp= &newconffiles; + push_cleanup(cu_fileslist,~0, 0, 0, 0); + strcpy(cidirrest,CONFFILESFILE); + conff= fopen(cidir,"r"); + if (conff) { + push_cleanup(cu_closefile,ehflag_bombout, 0,0, 1,(void*)conff); + while (fgets(conffilenamebuf,MAXCONFFILENAME-2,conff)) { + p= conffilenamebuf + strlen(conffilenamebuf); + assert(p != conffilenamebuf); + if (p[-1] != '\n') + ohshit(_("name of conffile (starting `%.250s') is too long (>%d characters)"), + conffilenamebuf, MAXCONFFILENAME); + while (p > conffilenamebuf && isspace(p[-1])) --p; + if (p == conffilenamebuf) continue; + *p= 0; + newconff= m_malloc(sizeof(struct fileinlist)); + newconff->next= 0; + newconff->namenode= findnamenode(conffilenamebuf, 0); + *newconffileslastp= newconff; + newconffileslastp= &newconff->next; + newconff->namenode->oldhash= NEWCONFFILEFLAG; + /* Let's see if any packages have this file. If they do we + * check to see if they listed it as a conffile, and if they did + * we copy the hash across. Since (for plain file conffiles, + * which is the only kind we are supposed to have) there will + * only be one package which `has' the file, this will usually + * mean we only look in the package which we're installing now. + * The `conffiles' data in the status file is ignored when a + * package isn't also listed in the file ownership database as + * having that file. If several packages are listed as owning + * the file we pick one at random. + */ + searchconff= 0; + for (packageslump= newconff->namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + otherpkg= packageslump->pkgs[i]; + debug(dbg_conffdetail,"process_archive conffile `%s' in package %s - conff ?", + newconff->namenode->name,otherpkg->name); + for (searchconff= otherpkg->installed.conffiles; + searchconff && strcmp(newconff->namenode->name,searchconff->name); + searchconff= searchconff->next) + debug(dbg_conffdetail, + "process_archive conffile `%s' in package %s - conff ? not `%s'", + newconff->namenode->name,otherpkg->name,searchconff->name); + if (searchconff) { + debug(dbg_conff,"process_archive conffile `%s' package=%s %s hash=%s", + newconff->namenode->name,otherpkg->name, + otherpkg == pkg ? "same" : "different!", + searchconff->hash); + if (otherpkg == pkg) goto xit_conff_hashcopy_srch; + } + } + } + xit_conff_hashcopy_srch: + if (searchconff) { + newconff->namenode->oldhash= searchconff->hash; + } else { + debug(dbg_conff,"process_archive conffile `%s' no package, no hash", + newconff->namenode->name); + } + newconff->namenode->flags |= fnnf_new_conff; + } + if (ferror(conff)) ohshite(_("read error in %.250s"),cidir); + pop_cleanup(ehflag_normaltidy); /* conff= fopen() */ + if (fclose(conff)) ohshite(_("error closing %.250s"),cidir); + } else { + if (errno != ENOENT) ohshite(_("error trying to open %.250s"),cidir); + } + + /* All the old conffiles are marked with a flag, so that we don't delete + * them if they seem to disappear completely. + */ + oldconffsetflags(pkg->installed.conffiles); + for (i = 0 ; i < cflict_index ; i++) { + oldconffsetflags(conflictor[i]->installed.conffiles); + } + + oldversionstatus= pkg->status; + + assert(oldversionstatus <= stat_configfiles); + debug(dbg_general,"process_archive oldversionstatus=%s", + statusstrings[oldversionstatus]); + + if (oldversionstatus == stat_halfconfigured || oldversionstatus == stat_installed) { + pkg->eflag |= eflagf_reinstreq; + pkg->status= stat_halfconfigured; + modstatdb_note(pkg); + push_cleanup(cu_prermupgrade,~ehflag_normaltidy, 0,0, 1,(void*)pkg); + maintainer_script_alternative(pkg, PRERMFILE, "pre-removal", cidir, cidirrest, + "upgrade", "failed-upgrade"); + pkg->status= stat_unpacked; + oldversionstatus= stat_unpacked; + modstatdb_note(pkg); + } + + for (i = 0 ; i < cflict_index ; i++) { + if (!(conflictor[i]->status == stat_halfconfigured || + conflictor[i]->status == stat_installed)) continue; + for (deconpil= deconfigure; deconpil; deconpil= deconpil->next) { + printf(_("De-configuring %s, so that we can remove %s ...\n"), + deconpil->pkg->name, conflictor[i]->name); + deconpil->pkg->status= stat_halfconfigured; + modstatdb_note(deconpil->pkg); + /* This means that we *either* go and run postinst abort-deconfigure, + * *or* queue the package for later configure processing, depending + * on which error cleanup route gets taken. + */ + push_cleanup(cu_prermdeconfigure,~ehflag_normaltidy, + ok_prermdeconfigure,ehflag_normaltidy, + 3,(void*)deconpil->pkg, + (void*)conflictor[i],(void*)pkg); + maintainer_script_installed(deconpil->pkg, PRERMFILE, "pre-removal", + "deconfigure", "in-favour", pkg->name, + versiondescribe(&pkg->available.version, + vdew_nonambig), + "removing", conflictor[i]->name, + versiondescribe(&conflictor[i]->installed.version, + vdew_nonambig), + (char*)0); + } + conflictor[i]->status= stat_halfconfigured; + modstatdb_note(conflictor[i]); + push_cleanup(cu_prerminfavour,~ehflag_normaltidy, 0,0, + 2,(void*)conflictor[i],(void*)pkg); + maintainer_script_installed(conflictor[i], PRERMFILE, "pre-removal", + "remove", "in-favour", pkg->name, + versiondescribe(&pkg->available.version, + vdew_nonambig), + (char*)0); + conflictor[i]->status= stat_halfinstalled; + modstatdb_note(conflictor[i]); + } + + pkg->eflag |= eflagf_reinstreq; + if (pkg->status == stat_notinstalled) + pkg->installed.version= pkg->available.version; + pkg->status= stat_halfinstalled; + modstatdb_note(pkg); + if (oldversionstatus == stat_notinstalled) { + push_cleanup(cu_preinstverynew,~ehflag_normaltidy, 0,0, + 3,(void*)pkg,(void*)cidir,(void*)cidirrest); + maintainer_script_new(pkg->name, PREINSTFILE, "pre-installation", cidir, cidirrest, + "install", (char*)0); + } else if (oldversionstatus == stat_configfiles) { + push_cleanup(cu_preinstnew,~ehflag_normaltidy, 0,0, + 3,(void*)pkg,(void*)cidir,(void*)cidirrest); + maintainer_script_new(pkg->name, PREINSTFILE, "pre-installation", cidir, cidirrest, + "install", versiondescribe(&pkg->installed.version, + vdew_nonambig), + (char*)0); + } else { + push_cleanup(cu_preinstupgrade,~ehflag_normaltidy, 0,0, + 4,(void*)pkg,(void*)cidir,(void*)cidirrest,(void*)&oldversionstatus); + maintainer_script_new(pkg->name, PREINSTFILE, "pre-installation", cidir, cidirrest, + "upgrade", versiondescribe(&pkg->installed.version, + vdew_nonambig), + (char*)0); + printf(_("Unpacking replacement %.250s ...\n"),pkg->name); + } + + /* + * Now we unpack the archive, backing things up as we go. + * For each file, we check to see if it already exists. + * There are several possibilities: + * + We are trying to install a non-directory ... + * - It doesn't exist. In this case we simply extract it. + * - It is a plain file, device, symlink, &c. We do an `atomic + * overwrite' using link() and rename(), but leave a backup copy. + * Later, when we delete the backup, we remove it from any other + * packages' lists. + * - It is a directory. In this case it depends on whether we're + * trying to install a symlink or something else. + * = If we're not trying to install a symlink we move the directory + * aside and extract the node. Later, when we recursively remove + * the backed-up directory, we remove it from any other packages' + * lists. + * = If we are trying to install a symlink we do nothing - ie, + * dpkg will never replace a directory tree with a symlink. This + * is to avoid embarrassing effects such as replacing a directory + * tree with a link to a link to the original directory tree. + * + We are trying to install a directory ... + * - It doesn't exist. We create it with the appropriate modes. + * - It exists as a directory or a symlink to one. We do nothing. + * - It is a plain file or a symlink (other than to a directory). + * We move it aside and create the directory. Later, when we + * delete the backup, we remove it from any other packages' lists. + * + * Install non-dir Install symlink Install dir + * Exists not X X X + * File/node/symlink LXR LXR BXR + * Directory BXR - - + * + * X: extract file/node/link/directory + * LX: atomic overwrite leaving backup + * B: ordinary backup + * R: later remove from other packages' lists + * -: do nothing + * + * After we've done this we go through the remaining things in the + * lists of packages we're trying to remove (including the old + * version of the current package). This happens in reverse order, + * so that we process files before the directories (or symlinks-to- + * directories) containing them. + * + If the thing is a conffile then we leave it alone for the purge + * operation. + * + Otherwise, there are several possibilities too: + * - The listed thing does not exist. We ignore it. + * - The listed thing is a directory or a symlink to a directory. + * We delete it only if it isn't listed in any other package. + * - The listed thing is not a directory, but was part of the package + * that was upgraded, we check to make sure the files aren't the + * same ones from the old package by checking dev/inode + * - The listed thing is not a directory or a symlink to one (ie, + * it's a plain file, device, pipe, &c, or a symlink to one, or a + * dangling symlink). We delete it. + * The removed packages' list becomes empty (of course, the new + * version of the package we're installing will have a new list, + * which replaces the old version's list). + * + * If at any stage we remove a file from a package's list, and the + * package isn't one we're already processing, and the package's + * list becomes empty as a result, we `vanish' the package. This + * means that we run its postrm with the `disappear' argument, and + * put the package in the `not-installed' state. Its conffiles are + * ignored and forgotten about. + * + * NOTE THAT THE OLD POSTRM IS RUN AFTER THE NEW PREINST, since the + * files get replaced `as we go'. + */ + + m_pipe(p1); + push_cleanup(cu_closepipe,ehflag_bombout, 0,0, 1,(void*)&p1[0]); + c1= m_fork(); + if (!c1) { + m_dup2(p1[1],1); close(p1[0]); close(p1[1]); + execlp(BACKEND, BACKEND, "--fsys-tarfile", filename, (char*)0); + ohshite(_("unable to exec dpkg-deb to get filesystem archive")); + } + close(p1[1]); + + newfileslist= 0; tc.newfilesp= &newfileslist; + push_cleanup(cu_fileslist,~0, 0, 0, 0); + tc.pkg= pkg; + tc.backendpipe= p1[0]; + push_cleanup(cu_closefd,~ehflag_bombout, 0,0, 1,&tc.backendpipe); + + r= TarExtractor((void*)&tc, &tf); + if (r) { + if (errno) { + ohshite(_("error reading dpkg-deb tar output")); + } else { + ohshite(_("corrupted filesystem tarfile - corrupted package archive")); + } + } + fd_null_copy(tc.backendpipe,-1,_("dpkg-deb: zap possible trailing zeros")); + close(tc.backendpipe); + waitsubproc(c1,BACKEND " --fsys-tarfile",PROCPIPE); + + if (oldversionstatus == stat_halfinstalled || oldversionstatus == stat_unpacked) { + /* Packages that were in `installed' and `postinstfailed' have been reduced + * to `unpacked' by now, by the running of the prerm script. + */ + pkg->status= stat_halfinstalled; + modstatdb_note(pkg); + push_cleanup(cu_postrmupgrade,~ehflag_normaltidy, 0,0, 1,(void*)pkg); + maintainer_script_alternative(pkg, POSTRMFILE, "post-removal", cidir, cidirrest, + "upgrade", "failed-upgrade"); + } + + /* If anything goes wrong while tidying up it's a bit late to do + * anything about it. However, we don't install the new status + * info yet, so that a future dpkg installation will put everything + * right (we hope). + * + * If something does go wrong later the `conflictor' package will be + * left in the `removal_failed' state. Removing or installing it + * will be impossible if it was required because of the conflict with + * the package we're installing now and (presumably) the dependency + * by other packages. This means that the files it contains in + * common with this package will hang around until we successfully + * get this package installed, after which point we can trust the + * conflicting package's file list, which will have been updated to + * remove any files in this package. + */ + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); + + /* Now we delete all the files that were in the old version of + * the package only, except (old or new) conffiles, which we leave + * alone. + */ + reversefilelist_init(&rlistit,pkg->clientdata->files); + while ((namenode= reversefilelist_next(&rlistit))) { + if ((namenode->flags & fnnf_old_conff) || + (namenode->flags & fnnf_new_conff) || + (namenode->flags & fnnf_new_inarchive)) + continue; + if (isdirectoryinuse(namenode,pkg)) continue; + fnamevb.used= fnameidlu; + varbufaddstr(&fnamevb, namenodetouse(namenode,pkg)->name); + varbufaddc(&fnamevb,0); + if (!rmdir(fnamevb.buf)) continue; + if (errno == ENOENT || errno == ELOOP) continue; + if (errno == ENOTDIR) { + /* Ok, it's an old file, but is it really not in the new package? + * We need to check to make sure, so we stat the file, then compare + * it to the new list. If we find a dev/inode match, we assume they + * are the same file, and leave it alone. NOTE: we don't check in + * other packages for sanity reasons (we don't want to stat _all_ + * the files on the system). + * + * We run down the list of _new_ files in this package. This keeps + * the process a little leaner. We are only worried about new ones + * since ones that stayed the same don't really apply here. + */ + struct stat oldfs; + int donotrm = 0; + /* If we can't stat the old or new file, or it's a directory, + * we leave it up to the normal code + */ + debug(dbg_eachfile, "process_archive: checking %s for same files on " + "upgrade/downgrade", fnamevb.buf); + if (!lstat(fnamevb.buf, &oldfs) && !S_ISDIR(oldfs.st_mode)) { + for (cfile = newfileslist; cfile; cfile = cfile->next) { + if (!cfile->namenode->filestat) { + cfile->namenode->filestat = (struct stat *) nfmalloc(sizeof(struct stat)); + if (lstat(cfile->namenode->name, cfile->namenode->filestat)) { + cfile->namenode->filestat= 0; + continue; + } + } + if (S_ISDIR(cfile->namenode->filestat->st_mode)) + continue; + if (oldfs.st_dev == cfile->namenode->filestat->st_dev && + oldfs.st_ino == cfile->namenode->filestat->st_ino) { + donotrm = 1; + debug(dbg_eachfile, "process_archive: not removing %s, since it matches %s", + fnamevb.buf, cfile->namenode->name); + } + } + } else + debug(dbg_eachfile, "process_archive: could not stat %s, skipping", fnamevb.buf); + if (donotrm) continue; + { + /* + * If file to remove is a device or s[gu]id, change its mode + * so that a malicious user cannot use it even if it's linked + * to another file. + */ + struct stat stat_buf; + if (stat(fnamevb.buf,&stat_buf)==0) { + if (S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode)) + chmod(fnamevb.buf, 0); + if (stat_buf.st_mode & (S_ISUID|S_ISGID)) + chmod(fnamevb.buf, stat_buf.st_mode & ~(S_ISUID|S_ISGID)); + } + } + if (!unlink(fnamevb.buf)) continue; + if (errno == ENOTDIR) continue; + } + fprintf(stderr, + _("dpkg: warning - unable to delete old file `%.250s': %s\n"), + namenode->name, strerror(errno)); + } + + /* OK, now we can write the updated files-in-this package list, + * since we've done away (hopefully) with all the old junk. + */ + write_filelist_except(pkg,newfileslist,0); + + /* We also install the new maintainer scripts, and any other + * cruft that may have come along with the package. First + * we go through the existing scripts replacing or removing + * them as appropriate; then we go through the new scripts + * (any that are left) and install them. + */ + debug(dbg_general, "process_archive updating info directory"); + varbufreset(&infofnvb); + varbufaddstr(&infofnvb,admindir); + varbufaddstr(&infofnvb,"/" INFODIR "/"); + infodirlen= infofnvb.used; + varbufaddc(&infofnvb,0); + dsd= opendir(infofnvb.buf); + if (!dsd) ohshite(_("cannot read info directory")); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + while ((de= readdir(dsd)) != 0) { + debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name); + if (de->d_name[0] == '.') continue; /* ignore dotfiles, including `.' and `..' */ + p= strrchr(de->d_name,'.'); if (!p) continue; /* ignore anything odd */ + if (strlen(pkg->name) != (size_t)(p-de->d_name) || + strncmp(de->d_name,pkg->name,p-de->d_name)) continue; + debug(dbg_stupidlyverbose, "process_archive info this pkg"); + /* Right do we have one ? */ + p++; /* skip past the full stop */ + if (!strcmp(p,LISTFILE)) continue; /* We do the list separately */ + if (strlen(p) > MAXCONTROLFILENAME) + ohshit(_("old version of package has overly-long info file name starting `%.250s'"), + de->d_name); + infofnvb.used= infodirlen; + varbufaddstr(&infofnvb,de->d_name); + varbufaddc(&infofnvb,0); + strcpy(cidirrest,p); + if (!rename(cidir,infofnvb.buf)) { + debug(dbg_scripts, "process_archive info installed %s as %s", + cidir, infofnvb.buf); + } else if (errno == ENOENT) { + /* Right, no new version. */ + if (unlink(infofnvb.buf)) + ohshite(_("unable to remove obsolete info file `%.250s'"),infofnvb.buf); + debug(dbg_scripts, "process_archive info unlinked %s",infofnvb.buf); + } else { + ohshite(_("unable to install (supposed) new info file `%.250s'"),cidir); + } + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + *cidirrest= 0; /* the directory itself */ + dsd= opendir(cidir); + if (!dsd) ohshite(_("unable to open temp control directory")); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + while ((de= readdir(dsd))) { + if (strchr(de->d_name,'.')) { + debug(dbg_scripts,"process_archive tmp.ci script/file `%s' contains dot", + de->d_name); + continue; + } + if (strlen(de->d_name) > MAXCONTROLFILENAME) + ohshit(_("package contains overly-long control info file name (starting `%.50s')"), + de->d_name); + strcpy(cidirrest,de->d_name); + /* First we check it's not a directory. */ + if (!rmdir(cidir)) + ohshit(_("package control info contained directory `%.250s'"),cidir); + else if (errno != ENOTDIR) + ohshite(_("package control info rmdir of `%.250s' didn't say not a dir"),de->d_name); + if (!strcmp(de->d_name,CONTROLFILE)) { + debug(dbg_scripts,"process_archive tmp.ci script/file `%s' is control",cidir); + continue; /* ignore the control file */ + } + if (!strcmp(de->d_name,LISTFILE)) { + fprintf(stderr, _("dpkg: warning - package %s" + " contained list as info file"), pkg->name); + continue; + } + /* Right, install it */ + newinfofilename= pkgadminfile(pkg,de->d_name); + if (rename(cidir,newinfofilename)) + ohshite(_("unable to install new info file `%.250s' as `%.250s'"), + cidir,newinfofilename); + debug(dbg_scripts,"process_archive tmp.ci script/file `%s' installed as `%s'", + cidir, newinfofilename); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + /* Update the status database. + * This involves copying each field across from the `available' + * to the `installed' half of the pkg structure. + * For some of the fields we have to do a complicated construction + * operation; for others we can just copy the value. + * We tackle the fields in the order they appear, so that + * we don't miss any out :-). + * At least we don't have to copy any strings that are referred + * to, because these are never modified and never freed. + */ + + /* The dependencies are the most difficult. We have to build + * a whole new forward dependency tree. At least the reverse + * links (linking our deppossi's into the reverse chains) + * can be done by copy_dependency_links. + */ + newdeplist= 0; newdeplistlastp= &newdeplist; + for (dep= pkg->available.depends; dep; dep= dep->next) { + newdep= nfmalloc(sizeof(struct dependency)); + newdep->up= pkg; + newdep->next= 0; + newdep->list= 0; newpossilastp= &newdep->list; + for (possi= dep->list; possi; possi= possi->next) { + newpossi= nfmalloc(sizeof(struct deppossi)); + newpossi->up= newdep; + newpossi->ed= possi->ed; + newpossi->next= 0; newpossi->nextrev= newpossi->backrev= 0; + newpossi->verrel= possi->verrel; + if (possi->verrel != dvr_none) newpossi->version= possi->version; + newpossi->cyclebreak= 0; + *newpossilastp= newpossi; + newpossilastp= &newpossi->next; + } + newdep->type= dep->type; + *newdeplistlastp= newdep; + newdeplistlastp= &newdep->next; + } + /* Right, now we've replicated the forward tree, we + * get copy_dependency_links to remove all the old dependency + * structures from the reverse links and add the new dependency + * structures in instead. It also copies the new dependency + * structure pointer for this package into the right field. + */ + copy_dependency_links(pkg,&pkg->installed.depends,newdeplist,0); + + /* The `depended' pointer in the structure doesn't represent anything + * that is actually specified by this package - it's there so we + * can find out what other packages refer to this one. So, + * we don't copy it. We go straight on to copy the text fields. + */ + pkg->installed.essential= pkg->available.essential; + pkg->installed.description= pkg->available.description; + pkg->installed.maintainer= pkg->available.maintainer; + pkg->installed.source= pkg->available.source; + pkg->installed.architecture= pkg->available.architecture; + pkg->installed.installedsize= pkg->available.installedsize; + pkg->installed.version= pkg->available.version; + pkg->installed.origin = pkg->available.origin; + pkg->installed.bugs = pkg->available.bugs; + + /* We have to generate our own conffiles structure. */ + pkg->installed.conffiles= 0; iconffileslastp= &pkg->installed.conffiles; + for (cfile= newconffiles; cfile; cfile= cfile->next) { + newiconff= nfmalloc(sizeof(struct conffile)); + newiconff->next= 0; + newiconff->name= nfstrsave(cfile->namenode->name); + newiconff->hash= nfstrsave(cfile->namenode->oldhash); + *iconffileslastp= newiconff; + iconffileslastp= &newiconff->next; + } + + /* We can just copy the arbitrary fields list, because it is + * never even rearragned. Phew ! + */ + pkg->installed.arbs= pkg->available.arbs; + + /* Check for disappearing packages: + * We go through all the packages on the system looking for ones + * whose files are entirely part of the one we've just unpacked + * (and which actually *have* some files!). + * + * Any that we find are removed - we run the postrm with `disappear' + * as an argument, and remove their info/... files and status info. + * Conffiles are ignored (the new package had better do something + * with them !). + */ + it= iterpkgstart(); + while ((otherpkg= iterpkgnext(it)) != 0) { + ensure_package_clientdata(otherpkg); + if (otherpkg == pkg || + otherpkg->status == stat_notinstalled || + otherpkg->status == stat_configfiles || + otherpkg->clientdata->istobe == itb_remove || + !otherpkg->clientdata->files) continue; + debug(dbg_veryverbose, "process_archive checking disappearance %s",otherpkg->name); + assert(otherpkg->clientdata->istobe == itb_normal || + otherpkg->clientdata->istobe == itb_deconfigure); + for (cfile= otherpkg->clientdata->files; + cfile && !strcmp(cfile->namenode->name,"/."); + cfile= cfile->next); + if (!cfile) { + debug(dbg_stupidlyverbose, "process_archive no non-root, no disappear"); + continue; + } + for (cfile= otherpkg->clientdata->files; + cfile && !filesavespackage(cfile,otherpkg,pkg); + cfile= cfile->next); + if (cfile) continue; + + /* So dependency things will give right answers ... */ + otherpkg->clientdata->istobe= itb_remove; + debug(dbg_veryverbose, "process_archive disappear checking dependencies"); + for (pdep= otherpkg->installed.depended; + pdep; + pdep= pdep->nextrev) { + if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends && + pdep->up->type != dep_recommends) continue; + if (depisok(pdep->up, &depprobwhy, 0,0)) continue; + varbufaddc(&depprobwhy,0); + debug(dbg_veryverbose,"process_archive cannot disappear: %s",depprobwhy.buf); + break; + } + if (!pdep) { + /* If we haven't found a reason not to yet, let's look some more. */ + for (providecheck= otherpkg->installed.depends; + providecheck; + providecheck= providecheck->next) { + if (providecheck->type != dep_provides) continue; + for (pdep= providecheck->list->ed->installed.depended; + pdep; + pdep= pdep->nextrev) { + if (pdep->up->type != dep_depends && pdep->up->type != dep_predepends && + pdep->up->type != dep_recommends) + continue; + if (depisok(pdep->up, &depprobwhy, 0,0)) continue; + varbufaddc(&depprobwhy,0); + debug(dbg_veryverbose,"process_archive cannot disappear (provides %s): %s", + providecheck->list->ed->name, depprobwhy.buf); + goto break_from_both_loops_at_once; + } + } + break_from_both_loops_at_once:; + } + otherpkg->clientdata->istobe= itb_normal; + if (pdep) continue; + + printf(_("(Noting disappearance of %s, which has been completely replaced.)\n"), + otherpkg->name); + debug(dbg_general, "process_archive disappearing %s",otherpkg->name); + /* No, we're disappearing it. This is the wrong time to go and + * run maintainer scripts and things, as we can't back out. But + * what can we do ? It has to be run this late. + */ + maintainer_script_installed(otherpkg, POSTRMFILE, + "post-removal script (for disappearance)", + "disappear", pkg->name, + versiondescribe(&pkg->available.version, + vdew_nonambig), + (char*)0); + + /* OK, now we delete all the stuff in the `info' directory .. */ + varbufreset(&fnvb); + varbufaddstr(&fnvb,admindir); + varbufaddstr(&fnvb,"/" INFODIR); + infodirbaseused= fnvb.used; + varbufaddc(&fnvb,0); + dsd= opendir(fnvb.buf); if (!dsd) ohshite(_("cannot read info directory")); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + + debug(dbg_general, "process_archive disappear cleaning info directory"); + + while ((de= readdir(dsd)) != 0) { + debug(dbg_veryverbose, "process_archive info file `%s'", de->d_name); + if (de->d_name[0] == '.') continue; + p= strrchr(de->d_name,'.'); if (!p) continue; + if (strlen(otherpkg->name) != (size_t)(p-de->d_name) || + strncmp(de->d_name,otherpkg->name,p-de->d_name)) continue; + debug(dbg_stupidlyverbose, "process_archive info this pkg"); + fnvb.used= infodirbaseused; + varbufaddstr(&fnvb,de->d_name); + varbufaddc(&fnvb,0); + if (unlink(fnvb.buf)) + ohshite(_("unable to delete disappearing control info file `%.250s'"),fnvb.buf); + debug(dbg_scripts, "process_archive info unlinked %s",fnvb.buf); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + otherpkg->status= stat_notinstalled; + otherpkg->want= want_purge; + otherpkg->eflag= eflagv_ok; + + otherpkg->installed.depends= 0; + otherpkg->installed.essential= 0; + otherpkg->installed.description= otherpkg->installed.maintainer= 0; + otherpkg->installed.installedsize= otherpkg->installed.source= 0; + otherpkg->installed.conffiles= 0; + blankversion(&otherpkg->installed.version); + otherpkg->installed.arbs= 0; + otherpkg->clientdata->fileslistvalid= 0; + + modstatdb_note(otherpkg); + + } /* while (otherpkg= ... */ + iterpkgend(it); + + /* Delete files from any other packages' lists. + * We have to do this before we claim this package is in any + * sane kind of state, as otherwise we might delete by mistake + * a file that we overwrote, when we remove the package which + * had the version we overwrote. To prevent this we make + * sure that we don't claim this package is OK until we + * have claimed `ownership' of all its files. + */ + for (cfile= newfileslist; cfile; cfile= cfile->next) { + if (!(cfile->namenode->flags & fnnf_elide_other_lists)) continue; + if (cfile->namenode->divert && cfile->namenode->divert->useinstead) { + divpkg= cfile->namenode->divert->pkg; + if (divpkg == pkg) { + debug(dbg_eachfile, + "process_archive not overwriting any `%s' (overriding, `%s')", + cfile->namenode->name, cfile->namenode->divert->useinstead->name); + continue; + } else { + debug(dbg_eachfile, + "process_archive looking for overwriting `%s' (overridden by %s)", + cfile->namenode->name, divpkg ? divpkg->name : "<local>"); + } + } else { + divpkg= 0; + debug(dbg_eachfile, "process_archive looking for overwriting `%s'", + cfile->namenode->name); + } + for (packageslump= cfile->namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + otherpkg= packageslump->pkgs[i]; + debug(dbg_eachfiledetail, "process_archive ... found in %s\n",otherpkg->name); + /* If !fileslistvalid then it's one of the disappeared packages above + * and we don't bother with it here, clearly. + */ + if (otherpkg == pkg || !otherpkg->clientdata->fileslistvalid) continue; + if (otherpkg == divpkg) { + debug(dbg_eachfiledetail, "process_archive ... diverted, skipping\n"); + continue; + } + + /* Found one. We delete remove the list entry for this file, + * (and any others in the same package) and then mark the package + * as requiring a reread. + */ + write_filelist_except(otherpkg, otherpkg->clientdata->files, 1); + ensure_package_clientdata(otherpkg); + debug(dbg_veryverbose, "process_archive overwrote from %s",otherpkg->name); + } + } + } + + /* Right, the package we've unpacked is now in a reasonable state. + * The only thing that we have left to do with it is remove + * backup files, and we can leave the user to fix that if and when + * it happens (we leave the reinstall required flag, of course). + */ + pkg->status= stat_unpacked; + modstatdb_note(pkg); + + /* Now we delete all the backup files that we made when + * extracting the archive - except for files listed as conffiles + * in the new package. + * This time we count it as an error if something goes wrong. + * + * Note that we don't ever delete things that were in the old + * package as a conffile and don't appear at all in the new. + */ + for (cfile= newfileslist; cfile; cfile= cfile->next) { + if (cfile->namenode->flags & fnnf_new_conff) continue; + fnametmpvb.used= fnameidlu; + varbufaddstr(&fnametmpvb,namenodetouse(cfile->namenode,pkg)->name); + varbufaddstr(&fnametmpvb,DPKGTEMPEXT); + varbufaddc(&fnametmpvb,0); + ensure_pathname_nonexisting(fnametmpvb.buf); + } + + /* OK, we're now fully done with the main package. + * This is quite a nice state, so we don't unwind past here. + */ + pkg->eflag= eflagv_ok; + modstatdb_note(pkg); + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); + + /* Only the removal of the conflictor left to do. + * The files list for the conflictor is still a little inconsistent in-core, + * as we have not yet updated the filename->packages mappings; however, + * the package->filenames mapping is + */ + for (i = 0 ; i < cflict_index ; i++) { + /* We need to have the most up-to-date info about which files are what ... */ + ensure_allinstfiles_available(); + removal_bulk(conflictor[i]); + } + + if (cipaction->arg == act_install) add_to_queue(pkg); +} diff --git a/src/query.c b/src/query.c new file mode 100644 index 000000000..8b43daf12 --- /dev/null +++ b/src/query.c @@ -0,0 +1,553 @@ +/* + * dpkg-query - program for query the dpkg database + * query.c - status enquiry and listing options + * + * dpkg - main program for package management + * enquiry.c - status enquiry and listing options + * + * Copyright (C) 1995,1996 Ian Jackson <ian@chiark.greenend.org.uk> + * Copyright (C) 200,2001 Wichert Akkerman <wakkerma@debian.org> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fnmatch.h> +#include <assert.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/termios.h> +#include <fcntl.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <myopt.h> + +#include "filesdb.h" +#include "main.h" + +static const char* showformat = "${Package}\t${Version}\n"; + + +void ensure_package_clientdata(struct pkginfo *pkg) { + if (pkg->clientdata) return; + pkg->clientdata= nfmalloc(sizeof(struct perpackagestate)); + pkg->clientdata->istobe= itb_normal; + pkg->clientdata->fileslistvalid= 0; + pkg->clientdata->files= 0; +} + +const char *pkgadminfile(struct pkginfo *pkg, const char *whichfile) { + static struct varbuf vb; + varbufreset(&vb); + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" INFODIR); + varbufaddstr(&vb,pkg->name); + varbufaddc(&vb,'.'); + varbufaddstr(&vb,whichfile); + varbufaddc(&vb,0); + return vb.buf; +} + +void cu_closepipe(int argc, void **argv) { + int *p1= (int*)argv[0]; + close(p1[0]); close(p1[1]); +} + +void cu_closefile(int argc, void **argv) { + FILE *f= (FILE*)(argv[0]); + fclose(f); +} + +void cu_closefd(int argc, void **argv) { + int ip= *(int*)argv; + close(ip); +} + +int pkglistqsortcmp(const void *a, const void *b) { + const struct pkginfo *pa= *(const struct pkginfo**)a; + const struct pkginfo *pb= *(const struct pkginfo**)b; + return strcmp(pa->name,pb->name); +} + +static void limiteddescription(struct pkginfo *pkg, int maxl, + const char **pdesc_r, int *l_r) { + const char *pdesc, *p; + int l; + + pdesc= pkg->installed.valid ? pkg->installed.description : 0; + if (!pdesc) pdesc= _("(no description available)"); + p= strchr(pdesc,'\n'); + if (!p) p= pdesc+strlen(pdesc); + l= (p - pdesc > maxl) ? maxl : (int)(p - pdesc); + *pdesc_r=pdesc; *l_r=l; +} + +static int getwidth(void) { + int fd; + int res; + struct winsize ws; + const char* columns; + + if ((columns=getenv("COLUMNS")) && ((res=atoi(columns))>0)) + ws.ws_col=res; + else if (!isatty(1)) + ws.ws_col=80; + else { + if ((fd=open("/dev/tty",O_RDONLY))!=-1) { + if (ioctl(fd, TIOCGWINSZ, &ws)==-1) + ws.ws_col=80; + close(fd); + } + } + return ws.ws_col; +} + +static void list1package(struct pkginfo *pkg, int *head) { + int l,w; + static int nw,vw,dw; + const char *pdesc; + static char format[80] = ""; + + if (format[0]==0) { + w=getwidth()-80; /* get spare width */ + if (w<0) w=0; /* lets not try to deal with terminals that are too small */ + w>>=2; /* halve that so we can add that to the both the name and description */ + nw=(14+w); /* name width */ + vw=(14+w); /* version width */ + dw=(44+(2*w)); /* description width */ + sprintf(format,"%%c%%c%%c %%-%d.%ds %%-%d.%ds %%.*s\n", nw, nw, vw, vw); + } + + if (!*head) { + fputs(_("\ +Desired=Unknown/Install/Remove/Purge/Hold\n\ +| Status=Not/Installed/Config-files/Unpacked/Failed-config/Half-installed\n\ +|/ Err?=(none)/Hold/Reinst-required/X=both-problems (Status,Err: uppercase=bad)\n"), stdout); + printf(format,'|','|','/', _("Name"), _("Version"), 40, _("Description")); + printf("+++-"); /* status */ + for (l=0;l<nw;l++) printf("="); printf("-"); /* packagename */ + for (l=0;l<vw;l++) printf("="); printf("-"); /* version */ + for (l=0;l<dw;l++) printf("="); /* description */ + printf("\n"); + *head= 1; + } + if (!pkg->installed.valid) blankpackageperfile(&pkg->installed); + limiteddescription(pkg,dw,&pdesc,&l); + printf(format, + "uihrp"[pkg->want], + "nUFiHc"[pkg->status], + " R?#"[pkg->eflag], + pkg->name, + versiondescribe(&pkg->installed.version,vdew_never), + l, pdesc); +} + +void listpackages(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + struct pkginfo **pkgl; + const char *thisarg; + int np, i, head, found; + + modstatdb_init(admindir,msdbrw_readonly); + + np= countpackages(); + pkgl= m_malloc(sizeof(struct pkginfo*)*np); + it= iterpkgstart(); i=0; + while ((pkg= iterpkgnext(it))) { + assert(i<np); + pkgl[i++]= pkg; + } + iterpkgend(it); + assert(i==np); + + qsort(pkgl,np,sizeof(struct pkginfo*),pkglistqsortcmp); + head=0; + + if (!*argv) { + for (i=0; i<np; i++) { + pkg= pkgl[i]; + if (pkg->status == stat_notinstalled) continue; + list1package(pkg,&head); + } + } else { + while ((thisarg= *argv++)) { + found= 0; + for (i=0; i<np; i++) { + pkg= pkgl[i]; + if (fnmatch(thisarg,pkg->name,0)) continue; + list1package(pkg,&head); found++; + } + if (!found) { + fprintf(stderr,_("No packages found matching %s.\n"),thisarg); + nerrs++; + } + } + } + if (ferror(stdout)) werr("stdout"); + if (ferror(stderr)) werr("stderr"); + modstatdb_shutdown(); +} + +static int searchoutput(struct filenamenode *namenode) { + int found, i; + struct filepackages *packageslump; + + if (namenode->divert) { + for (i=0; i<2; i++) { + if (namenode->divert->pkg) printf(_("diversion by %s"),namenode->divert->pkg->name); + else printf(_("local diversion")); + printf(" %s: %s\n", i ? _("to") : _("from"), + i ? + (namenode->divert->useinstead + ? namenode->divert->useinstead->name + : namenode->name) + : + (namenode->divert->camefrom + ? namenode->divert->camefrom->name + : namenode->name)); + } + } + found= 0; + for (packageslump= namenode->packages; + packageslump; + packageslump= packageslump->more) { + for (i=0; i < PERFILEPACKAGESLUMP && packageslump->pkgs[i]; i++) { + if (found) fputs(", ",stdout); + fputs(packageslump->pkgs[i]->name,stdout); + found++; + } + } + if (found) printf(": %s\n",namenode->name); + return found + (namenode->divert ? 1 : 0); +} + +void searchfiles(const char *const *argv) { + struct filenamenode *namenode; + struct fileiterator *it; + const char *thisarg; + int found; + static struct varbuf vb; + + if (!*argv) + badusage(_("--search needs at least one file name pattern argument")); + + modstatdb_init(admindir,msdbrw_readonly|msdbrw_noavail); + ensure_allinstfiles_available_quiet(); + ensure_diversions(); + + while ((thisarg= *argv++) != 0) { + found= 0; + if (!strchr("*[?/",*thisarg)) { + varbufreset(&vb); + varbufaddc(&vb,'*'); + varbufaddstr(&vb,thisarg); + varbufaddc(&vb,'*'); + varbufaddc(&vb,0); + thisarg= vb.buf; + } + if (strcspn(thisarg,"*[?\\") == strlen(thisarg)) { + namenode= findnamenode(thisarg, 0); + found += searchoutput(namenode); + } else { + it= iterfilestart(); + while ((namenode= iterfilenext(it)) != 0) { + if (fnmatch(thisarg,namenode->name,0)) continue; + found+= searchoutput(namenode); + } + iterfileend(it); + } + if (!found) { + fprintf(stderr,_("dpkg: %s not found.\n"),thisarg); + nerrs++; + if (ferror(stderr)) werr("stderr"); + } else { + if (ferror(stdout)) werr("stdout"); + } + } + modstatdb_shutdown(); +} + +void enqperpackage(const char *const *argv) { + int failures; + const char *thisarg; + struct fileinlist *file; + struct pkginfo *pkg; + struct filenamenode *namenode; + + if (!*argv) + badusage(_("--%s needs at least one package name argument"), cipaction->olong); + + failures= 0; + if (cipaction->arg==act_listfiles) + modstatdb_init(admindir,msdbrw_readonly|msdbrw_noavail); + else + modstatdb_init(admindir,msdbrw_readonly); + + while ((thisarg= *argv++) != 0) { + pkg= findpackage(thisarg); + + switch (cipaction->arg) { + + case act_status: + if (pkg->status == stat_notinstalled && + pkg->priority == pri_unknown && + !(pkg->section && *pkg->section) && + !pkg->files && + pkg->want == want_unknown && + !informative(pkg,&pkg->installed)) { + fprintf(stderr,_("Package `%s' is not installed and no info is available.\n"),pkg->name); + failures++; + } else { + writerecord(stdout, "<stdout>", pkg, &pkg->installed); + } + break; + + case act_printavail: + if (!informative(pkg,&pkg->available)) { + fprintf(stderr,_("Package `%s' is not available.\n"),pkg->name); + failures++; + } else { + writerecord(stdout, "<stdout>", pkg, &pkg->available); + } + break; + + case act_listfiles: + switch (pkg->status) { + case stat_notinstalled: + fprintf(stderr,_("Package `%s' is not installed.\n"),pkg->name); + failures++; + break; + + default: + ensure_packagefiles_available(pkg); + ensure_diversions(); + file= pkg->clientdata->files; + if (!file) { + printf(_("Package `%s' does not contain any files (!)\n"),pkg->name); + } else { + while (file) { + namenode= file->namenode; + puts(namenode->name); + if (namenode->divert && !namenode->divert->camefrom) { + if (!namenode->divert->pkg) printf(_("locally diverted")); + else if (pkg == namenode->divert->pkg) printf(_("package diverts others")); + else printf(_("diverted by %s"),namenode->divert->pkg->name); + printf(_(" to: %s\n"),namenode->divert->useinstead->name); + } + file= file->next; + } + } + break; + } + break; + + default: + internerr("unknown action"); + } + + putchar('\n'); + if (ferror(stdout)) werr("stdout"); + } + + if (failures) { + nerrs++; + fputs(_("Use dpkg --info (= dpkg-deb --info) to examine archive files,\n" + "and dpkg --contents (= dpkg-deb --contents) to list their contents.\n"),stderr); + if (ferror(stdout)) werr("stdout"); + } + modstatdb_shutdown(); +} + +void showpackages(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + struct pkginfo **pkgl; + const char *thisarg; + int np, i, found; + struct lstitem* fmt = parseformat(showformat); + + if (!fmt) { + nerrs++; + return; + } + + modstatdb_init(admindir,msdbrw_readonly); + + np= countpackages(); + pkgl= m_malloc(sizeof(struct pkginfo*)*np); + it= iterpkgstart(); i=0; + while ((pkg= iterpkgnext(it))) { + assert(i<np); + pkgl[i++]= pkg; + } + iterpkgend(it); + assert(i==np); + + qsort(pkgl,np,sizeof(struct pkginfo*),pkglistqsortcmp); + + if (!*argv) { + for (i=0; i<np; i++) { + pkg= pkgl[i]; + if (pkg->status == stat_notinstalled) continue; + show1package(fmt,pkg); + } + } else { + while ((thisarg= *argv++)) { + found= 0; + for (i=0; i<np; i++) { + pkg= pkgl[i]; + if (fnmatch(thisarg,pkg->name,0)) continue; + show1package(fmt,pkg); found++; + } + if (!found) { + fprintf(stderr,_("No packages found matching %s.\n"),thisarg); + nerrs++; + } + } + } + if (ferror(stdout)) werr("stdout"); + if (ferror(stderr)) werr("stderr"); + freeformat(fmt); + modstatdb_shutdown(); +} + +static void printversion(void) { + if (fputs(_("Debian `"), stdout) < 0) werr("stdout"); + if (fputs(DPKGQUERY, stdout) < 0) werr("stdout"); + if (fputs(_("' package management program query tool\n"), stdout) < 0) + werr("stdout"); + if (fputs(_( "This is free software; see the GNU General Public Licence version 2 or\n" + "later for copying conditions. There is NO warranty.\n" + "See " DPKGQUERY " --licence for copyright and license details.\n"), + stdout) < 0) werr("stdout"); +} +/* + options that need fixing: + dpkg --yet-to-unpack \n\ + */ +static void usage(void) { + if (fprintf (stdout, _("\ +Usage: " DPKGQUERY " [<option>] <command>\n\ +Commands:\n\ + -s|--status <package-name> ... display package status details\n\ + -p|--print-avail <package-name> ... display available version details\n\ + -L|--listfiles <package-name> ... list files `owned' by package(s)\n\ + -l|--list [<pattern> ...] list packages concisely\n\ + -W|--show <pattern> ... show information on package(s)\n\ + -S|--search <pattern> ... find package(s) owning file(s)\n\ + --help | --version show this help / version number\n\ + --licence print copyright licensing terms\n\ +\n\ +Options:\n\ + --admindir=<directory> Use <directory> instead of %s\n\ + -f|--showformat=<format> Use alternative format for --show\n\ +\n\ +Format syntax:\n\ + A format is a string that will be output for each package. The format\n\ + can include the standard escape sequences \\n (newline), \\r (carriage\n\ + return) or \\\\ (plain backslash). Package information can be included\n\ + by inserting variable references to package fields using the ${var[;width]}\n\ + syntax. Fields will be right-aligned unless the width is negative in which\n\ + case left aligenment will be used. \n\ +"), + ADMINDIR) < 0) werr ("stdout"); +} + +const char thisname[]= "dpkg-query"; +const char printforhelp[]= N_("\ +Use --help for help about querying packages;\n\ +Use --licence for copyright licence and lack of warranty (GNU GPL).\n\ +\n"); + +const struct cmdinfo *cipaction= 0; +int f_pending=0, f_recursive=0, f_alsoselect=1, f_skipsame=0, f_noact=0; +int f_autodeconf=0, f_nodebsig=0; +unsigned long f_debug=0; +/* Change fc_overwrite to 1 to enable force-overwrite by default */ +int fc_hold=0; +int fc_conflicts=0, fc_depends=0; +int fc_badpath=0; + +int errabort = 50; +const char *admindir= ADMINDIR; +const char *instdir= ""; +struct packageinlist *ignoredependss=0; + +static void helponly(const struct cmdinfo *cip, const char *value) NONRETURNING; +static void helponly(const struct cmdinfo *cip, const char *value) { + usage(); exit(0); +} +static void versiononly(const struct cmdinfo *cip, const char *value) NONRETURNING; +static void versiononly(const struct cmdinfo *cip, const char *value) { + printversion(); exit(0); +} + +static void setaction(const struct cmdinfo *cip, const char *value) { + if (cipaction) + badusage(_("conflicting actions --%s and --%s"),cip->olong,cipaction->olong); + cipaction= cip; +} + +static const struct cmdinfo cmdinfos[]= { + /* This table has both the action entries in it and the normal options. + * The action entries are made with the ACTION macro, as they all + * have a very similar structure. + */ +#define ACTION(longopt,shortopt,code,function) \ + { longopt, shortopt, 0,0,0, setaction, code, 0, (voidfnp)function } +#define OBSOLETE(longopt,shortopt) \ + { longopt, shortopt, 0,0,0, setobsolete, 0, 0, 0 } + + ACTION( "listfiles", 'L', act_listfiles, enqperpackage ), + ACTION( "status", 's', act_status, enqperpackage ), + ACTION( "print-avail", 'p', act_printavail, enqperpackage ), + ACTION( "list", 'l', act_listpackages, listpackages ), + ACTION( "search", 'S', act_searchfiles, searchfiles ), + ACTION( "show", 'W', act_listpackages, showpackages ), + + { "admindir", 0, 1, 0, &admindir, 0 }, + { "showformat", 'f', 1, 0, &showformat, 0 }, + { "help", 'h', 0, 0, 0, helponly }, + { "version", 0, 0, 0, 0, versiononly }, + { "licence",/* UK spelling */ 0,0,0,0, showcopyright }, + { "license",/* US spelling */ 0,0,0,0, showcopyright }, + { 0, 0 } +}; + +int main(int argc, const char *const *argv) { + jmp_buf ejbuf; + static void (*actionfunction)(const char *const *argv); + + standard_startup(&ejbuf, argc, &argv, NULL, 0, cmdinfos); + if (!cipaction) badusage(_("need an action option")); + + setvbuf(stdout,0,_IONBF,0); + filesdbinit(); + + actionfunction= (void (*)(const char* const*))cipaction->farg; + + actionfunction(argv); + + standard_shutdown(0); + + return reportbroken_retexitstatus(); +} diff --git a/src/remove.c b/src/remove.c new file mode 100644 index 000000000..6dbf1a18c --- /dev/null +++ b/src/remove.c @@ -0,0 +1,584 @@ +/* Change: separate removal_bulk handling of halfinstalled or unpacked pkgs + * (ie, remove the real files in the .deb) into its own function. + * Note that "installed" state is converted to unpacked by + * deferred_remove() or halfinstalled by process_archive()) + * + * Change: separate purging of configfiles and running of postrm to its + * own function. + * + * Change: retry removing directories after postrm purge is finished, to + * handle directories that had conffiles or config files and so + * forth in them. also only warn about non-empty directories at + * that point. + */ + +/* + * dpkg - main program for package management + * remove.c - functionality for removing packages + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <dirent.h> +#include <ctype.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <myopt.h> + +#include "filesdb.h" +#include "main.h" + +static void checkforremoval(struct pkginfo *pkgtoremove, + struct pkginfo *pkgdepcheck, /* may be virtual pkg */ + int *rokp, struct varbuf *raemsgs) { + struct deppossi *possi; + struct pkginfo *depender; + int before, ok; + + for (possi= pkgdepcheck->installed.depended; possi; possi= possi->nextrev) { + if (possi->up->type != dep_depends && possi->up->type != dep_predepends) continue; + depender= possi->up->up; + debug(dbg_depcon,"checking depending package `%s'",depender->name); + if (depender->status != stat_installed) continue; + if (ignore_depends(depender)) { + debug(dbg_depcon,"ignoring depending package `%s'\n",depender->name); + continue; + } + if (dependtry > 1) { if (findbreakcycle(pkgtoremove)) sincenothing= 0; } + before= raemsgs->used; + ok= dependencies_ok(depender,pkgtoremove,raemsgs); + if (ok == 0 && depender->clientdata->istobe == itb_remove) ok= 1; + if (ok == 1) raemsgs->used= before; /* Don't burble about reasons for deferral */ + if (ok < *rokp) *rokp= ok; + } +} + +void deferred_remove(struct pkginfo *pkg) { + struct varbuf raemsgs; + int rok; + struct dependency *dep; + + debug(dbg_general,"deferred_remove package %s",pkg->name); + + if (pkg->status == stat_notinstalled) { + fprintf(stderr, + _("dpkg - warning: ignoring request to remove %.250s which isn't installed.\n"), + pkg->name); + pkg->clientdata->istobe= itb_normal; + return; + } else if (!f_pending && + pkg->status == stat_configfiles && + cipaction->arg != act_purge) { + fprintf(stderr, + _("dpkg - warning: ignoring request to remove %.250s, only the config\n" + " files of which are on the system. Use --purge to remove them too.\n"), + pkg->name); + pkg->clientdata->istobe= itb_normal; + return; + } + + assert(pkg->installed.valid); + if (pkg->installed.essential && pkg->status != stat_configfiles) + forcibleerr(fc_removeessential, _("This is an essential package -" + " it should not be removed.")); + + if (!f_pending) + pkg->want= (cipaction->arg == act_purge) ? want_purge : want_deinstall; + if (!f_noact) modstatdb_note(pkg); + + debug(dbg_general,"checking dependencies for remove `%s'",pkg->name); + varbufinit(&raemsgs); + rok= 2; + checkforremoval(pkg,pkg,&rok,&raemsgs); + for (dep= pkg->installed.depends; dep; dep= dep->next) { + if (dep->type != dep_provides) continue; + debug(dbg_depcon,"checking virtual package `%s'",dep->list->ed->name); + checkforremoval(pkg,dep->list->ed,&rok,&raemsgs); + } + + if (rok == 1) { + varbuffree(&raemsgs); + pkg->clientdata->istobe= itb_remove; + add_to_queue(pkg); + return; + } else if (rok == 0) { + sincenothing= 0; + varbufaddc(&raemsgs,0); + fprintf(stderr, + _("dpkg: dependency problems prevent removal of %s:\n%s"), + pkg->name, raemsgs.buf); + ohshit(_("dependency problems - not removing")); + } else if (raemsgs.used) { + varbufaddc(&raemsgs,0); + fprintf(stderr, + _("dpkg: %s: dependency problems, but removing anyway as you request:\n%s"), + pkg->name, raemsgs.buf); + } + varbuffree(&raemsgs); + sincenothing= 0; + + if (pkg->eflag & eflagf_reinstreq) + forcibleerr(fc_removereinstreq, + _("Package is in a very bad inconsistent state - you should\n" + " reinstall it before attempting a removal.")); + + ensure_allinstfiles_available(); + filesdbinit(); + + if (f_noact) { + printf(_("Would remove or purge %s ...\n"),pkg->name); + pkg->status= stat_notinstalled; + pkg->clientdata->istobe= itb_normal; + return; + } + + oldconffsetflags(pkg->installed.conffiles); + + printf(_("Removing %s ...\n"),pkg->name); + if (pkg->status == stat_halfconfigured || pkg->status == stat_installed) { + + if (pkg->status == stat_installed || pkg->status == stat_halfconfigured) { + pkg->status= stat_halfconfigured; + modstatdb_note(pkg); + push_cleanup(cu_prermremove,~ehflag_normaltidy, 0,0, 1,(void*)pkg); + maintainer_script_installed(pkg, PRERMFILE, "pre-removal", + "remove", (char*)0); + } + + pkg->status= stat_unpacked; /* Will turn into halfinstalled soon ... */ + } + + removal_bulk(pkg); +} + +static void push_leftover(struct fileinlist **leftoverp, + struct filenamenode *namenode) { + struct fileinlist *newentry; + newentry= nfmalloc(sizeof(struct fileinlist)); + newentry->next= *leftoverp; + newentry->namenode= namenode; + *leftoverp= newentry; +} + +static void removal_bulk_remove_files( + struct pkginfo *pkg, + int *out_foundpostrm) +{ + int before; + int infodirbaseused; + struct reversefilelistiter rlistit; + struct fileinlist *leftover; + struct filenamenode *namenode; + static struct varbuf fnvb; + DIR *dsd; + struct dirent *de; + struct stat stab; + + pkg->status= stat_halfinstalled; + modstatdb_note(pkg); + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); + + reversefilelist_init(&rlistit,pkg->clientdata->files); + leftover= 0; + while ((namenode= reversefilelist_next(&rlistit))) { + debug(dbg_eachfile, "removal_bulk `%s' flags=%o", + namenode->name, namenode->flags); + if (namenode->flags & fnnf_old_conff) { + push_leftover(&leftover,namenode); + continue; + } + varbufreset(&fnvb); + varbufaddstr(&fnvb,instdir); + varbufaddstr(&fnvb,namenodetouse(namenode,pkg)->name); + before= fnvb.used; + + varbufaddstr(&fnvb,DPKGTEMPEXT); + varbufaddc(&fnvb,0); + debug(dbg_eachfiledetail, "removal_bulk cleaning temp `%s'", fnvb.buf); + + ensure_pathname_nonexisting(fnvb.buf); + + fnvb.used= before; + varbufaddstr(&fnvb,DPKGNEWEXT); + varbufaddc(&fnvb,0); + debug(dbg_eachfiledetail, "removal_bulk cleaning new `%s'", fnvb.buf); + ensure_pathname_nonexisting(fnvb.buf); + + fnvb.used= before; + varbufaddc(&fnvb,0); + if (!stat(fnvb.buf,&stab) && S_ISDIR(stab.st_mode)) { + debug(dbg_eachfiledetail, "removal_bulk is a directory"); + /* Only delete a directory or a link to one if we're the only + * package which uses it. Other files should only be listed + * in this package (but we don't check). + */ + if (isdirectoryinuse(namenode,pkg)) continue; + } + debug(dbg_eachfiledetail, "removal_bulk removing `%s'", fnvb.buf); + if (!rmdir(fnvb.buf) || errno == ENOENT || errno == ELOOP) continue; + if (errno == ENOTEMPTY) { + push_leftover(&leftover,namenode); + continue; + } else if (errno == EBUSY || errno == EPERM) { + fprintf(stderr, _("dpkg - warning: while removing %.250s," + " unable to remove directory `%.250s':" + " %s - directory may be a mount point ?\n"), + pkg->name, namenode->name, strerror(errno)); + push_leftover(&leftover,namenode); + continue; + } + if (errno != ENOTDIR) ohshite(_("cannot remove `%.250s'"),fnvb.buf); + debug(dbg_eachfiledetail, "removal_bulk unlinking `%s'", fnvb.buf); + { + /* + * If file to remove is a device or s[gu]id, change its mode + * so that a malicious user cannot use it even if it's linked + * to another file + */ + struct stat stat_buf; + if (stat(fnvb.buf,&stat_buf)==0) { + if (S_ISCHR(stat_buf.st_mode) || S_ISBLK(stat_buf.st_mode)) { + chmod(fnvb.buf,0); + } + if (stat_buf.st_mode & (S_ISUID|S_ISGID)) { + chmod(fnvb.buf,stat_buf.st_mode & ~(S_ISUID|S_ISGID)); + } + } + } + if (unlink(fnvb.buf)) ohshite(_("cannot remove file `%.250s'"),fnvb.buf); + } + write_filelist_except(pkg,leftover,0); + maintainer_script_installed(pkg, POSTRMFILE, "post-removal", + "remove", (char*)0); + varbufreset(&fnvb); + varbufaddstr(&fnvb,admindir); + varbufaddstr(&fnvb,"/" INFODIR); + infodirbaseused= fnvb.used; + varbufaddc(&fnvb,0); + dsd= opendir(fnvb.buf); if (!dsd) ohshite(_("cannot read info directory")); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + *out_foundpostrm= 0; + + debug(dbg_general, "removal_bulk cleaning info directory"); + + while ((de= readdir(dsd)) != 0) { + char *p; + + debug(dbg_veryverbose, "removal_bulk info file `%s'", de->d_name); + if (de->d_name[0] == '.') continue; + p= strrchr(de->d_name,'.'); if (!p) continue; + if (strlen(pkg->name) != (size_t)(p-de->d_name) || + strncmp(de->d_name,pkg->name,p-de->d_name)) continue; + debug(dbg_stupidlyverbose, "removal_bulk info this pkg"); + /* We need the postrm and list files for --purge. */ + if (!strcmp(p+1,LISTFILE)) continue; + if (!strcmp(p+1,POSTRMFILE)) { *out_foundpostrm=1; continue; } + debug(dbg_stupidlyverbose, "removal_bulk info not postrm or list"); + fnvb.used= infodirbaseused; + varbufaddstr(&fnvb,de->d_name); + varbufaddc(&fnvb,0); + if (unlink(fnvb.buf)) + ohshite(_("unable to delete control info file `%.250s'"),fnvb.buf); + debug(dbg_scripts, "removal_bulk info unlinked %s",fnvb.buf); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + pkg->status= stat_configfiles; + pkg->installed.essential= 0; + modstatdb_note(pkg); + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); +} + +static void removal_bulk_remove_leftover_dirs(struct pkginfo *pkg) { + struct reversefilelistiter rlistit; + struct fileinlist *leftover; + struct filenamenode *namenode; + static struct varbuf fnvb; + struct stat stab; + + modstatdb_note(pkg); + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); + + reversefilelist_init(&rlistit,pkg->clientdata->files); + leftover= 0; + while ((namenode= reversefilelist_next(&rlistit))) { + debug(dbg_eachfile, "removal_bulk `%s' flags=%o", + namenode->name, namenode->flags); + if (namenode->flags & fnnf_old_conff) { + push_leftover(&leftover,namenode); + continue; + } + + varbufreset(&fnvb); + varbufaddstr(&fnvb,instdir); + varbufaddstr(&fnvb,namenodetouse(namenode,pkg)->name); + varbufaddc(&fnvb,0); + + if (!stat(fnvb.buf,&stab) && S_ISDIR(stab.st_mode)) { + debug(dbg_eachfiledetail, "removal_bulk is a directory"); + /* Only delete a directory or a link to one if we're the only + * package which uses it. Other files should only be listed + * in this package (but we don't check). + */ + if (isdirectoryinuse(namenode,pkg)) continue; + } + + debug(dbg_eachfiledetail, "removal_bulk removing `%s'", fnvb.buf); + if (!rmdir(fnvb.buf) || errno == ENOENT || errno == ELOOP) continue; + if (errno == ENOTEMPTY) { + fprintf(stderr, + _("dpkg - warning: while removing %.250s, directory `%.250s' not empty " + "so not removed.\n"), + pkg->name, namenode->name); + push_leftover(&leftover,namenode); + continue; + } else if (errno == EBUSY || errno == EPERM) { + fprintf(stderr, _("dpkg - warning: while removing %.250s," + " unable to remove directory `%.250s':" + " %s - directory may be a mount point ?\n"), + pkg->name, namenode->name, strerror(errno)); + push_leftover(&leftover,namenode); + continue; + } + if (errno != ENOTDIR) ohshite(_("cannot remove `%.250s'"),fnvb.buf); + + push_leftover(&leftover,namenode); + continue; + } + write_filelist_except(pkg,leftover,0); + + modstatdb_note(pkg); + push_checkpoint(~ehflag_bombout, ehflag_normaltidy); +} + +static void removal_bulk_remove_configfiles(struct pkginfo *pkg) { + static const char *const removeconffexts[]= { REMOVECONFFEXTS, 0 }; + int r, removevbbase; + int conffnameused, conffbasenamelen; + char *conffbasename; + struct conffile *conff, **lconffp; + struct fileinlist *searchfile; + DIR *dsd; + struct dirent *de; + char *p; + const char *const *ext; + + printf(_("Purging configuration files for %s ...\n"),pkg->name); + ensure_packagefiles_available(pkg); /* We may have modified this above. */ + + /* We're about to remove the configuration, so remove the note + * about which version it was ... + */ + blankversion(&pkg->configversion); + modstatdb_note(pkg); + + /* Remove from our list any conffiles that aren't ours any more or + * are involved in diversions, except if we are the package doing the + * diverting. + */ + for (lconffp= &pkg->installed.conffiles; (conff= *lconffp) != 0; ) { + for (searchfile= pkg->clientdata->files; + searchfile && strcmp(searchfile->namenode->name,conff->name); + searchfile= searchfile->next); + if (!searchfile) { + debug(dbg_conff,"removal_bulk conffile not ours any more `%s'",conff->name); + *lconffp= conff->next; + } else if (searchfile->namenode->divert && + (searchfile->namenode->divert->camefrom || + (searchfile->namenode->divert->useinstead && + searchfile->namenode->divert->pkg != pkg))) { + debug(dbg_conff,"removal_bulk conffile `%s' ignored due to diversion\n", + conff->name); + *lconffp= conff->next; + } else { + debug(dbg_conffdetail,"removal_bulk set to new conffile `%s'",conff->name); + conff->hash= NEWCONFFILEFLAG; /* yes, cast away const */ + lconffp= &conff->next; + } + } + modstatdb_note(pkg); + + for (conff= pkg->installed.conffiles; conff; conff= conff->next) { + static struct varbuf fnvb, removevb; + varbufreset(&fnvb); + r= conffderef(pkg, &fnvb, conff->name); + debug(dbg_conffdetail, "removal_bulk conffile `%s' (= `%s')", + conff->name, r == -1 ? "<r==-1>" : fnvb.buf); + if (r == -1) continue; + conffnameused= fnvb.used-1; + if (unlink(fnvb.buf) && errno != ENOENT && errno != ENOTDIR) + ohshite(_("cannot remove old config file `%.250s' (= `%.250s')"), + conff->name, fnvb.buf); + p= strrchr(fnvb.buf,'/'); if (!p) continue; + *p= 0; + varbufreset(&removevb); + varbufaddstr(&removevb,fnvb.buf); + varbufaddc(&removevb,'/'); + removevbbase= removevb.used; + varbufaddc(&removevb,0); + dsd= opendir(removevb.buf); + if (!dsd) { + int e=errno; + debug(dbg_conffdetail, "removal_bulk conffile no dsd %s %s", + fnvb.buf, strerror(e)); errno= e; + if (errno == ENOENT || errno == ENOTDIR) continue; + ohshite(_("cannot read config file dir `%.250s' (from `%.250s')"), + fnvb.buf, conff->name); + } + debug(dbg_conffdetail, "removal_bulk conffile cleaning dsd %s", fnvb.buf); + push_cleanup(cu_closedir,~0, 0,0, 1,(void*)dsd); + *p= '/'; + conffbasenamelen= strlen(++p); + conffbasename= fnvb.buf+conffnameused-conffbasenamelen; + while ((de= readdir(dsd)) != 0) { + debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry=`%s'" + " conffbasename=`%s' conffnameused=%d conffbasenamelen=%d", + de->d_name, conffbasename, conffnameused, conffbasenamelen); + if (!strncmp(de->d_name,conffbasename,conffbasenamelen)) { + debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry starts right"); + for (ext= removeconffexts; *ext; ext++) + if (!strcmp(*ext,de->d_name+conffbasenamelen)) goto yes_remove; + p= de->d_name+conffbasenamelen; + if (*p++ == '~') { + while (*p && cisdigit(*p)) p++; + if (*p == '~' && !*++p) goto yes_remove; + } + } + debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry starts wrong"); + if (de->d_name[0] == '#' && + !strncmp(de->d_name+1,conffbasename,conffbasenamelen) && + !strcmp(de->d_name+1+conffbasenamelen,"#")) + goto yes_remove; + debug(dbg_stupidlyverbose, "removal_bulk conffile dsd entry not it"); + continue; + yes_remove: + removevb.used= removevbbase; + varbufaddstr(&removevb,de->d_name); varbufaddc(&removevb,0); + debug(dbg_conffdetail, "removal_bulk conffile dsd entry removing `%s'", + removevb.buf); + if (unlink(removevb.buf) && errno != ENOENT && errno != ENOTDIR) + ohshite(_("cannot remove old backup config file `%.250s' (of `%.250s')"), + removevb.buf, conff->name); + } + pop_cleanup(ehflag_normaltidy); /* closedir */ + + } + + pkg->installed.conffiles= 0; + modstatdb_note(pkg); + + maintainer_script_installed(pkg, POSTRMFILE, "post-removal", + "purge", (char*)0); +} + +void removal_bulk(struct pkginfo *pkg) { + /* This is used both by deferred_remove in this file, and at + * the end of process_archive in archives.c if it needs to finish + * removing a conflicting package. + */ + + int pkgnameused; + int foundpostrm= 0; + const char *postrmfilename; + + debug(dbg_general,"removal_bulk package %s",pkg->name); + + if (pkg->status == stat_halfinstalled || pkg->status == stat_unpacked) { + + removal_bulk_remove_files(pkg, &foundpostrm); + + } else { + struct stat stab; + + postrmfilename= pkgadminfile(pkg,POSTRMFILE); + if (!lstat(postrmfilename,&stab)) foundpostrm= 1; + else if (errno == ENOENT) foundpostrm= 0; + else ohshite(_("unable to check existence of `%.250s'"),postrmfilename); + + } + + debug(dbg_general, "removal_bulk purging? foundpostrm=%d",foundpostrm); + + if (!foundpostrm && !pkg->installed.conffiles) { + /* If there are no config files and no postrm script then we + * go straight into `purge'. + */ + debug(dbg_general, "removal_bulk no postrm, no conffiles, purging"); + pkg->want= want_purge; + + } else if (pkg->want == want_purge) { + + removal_bulk_remove_configfiles(pkg); + + } + + if (pkg->want == want_purge) { + /* ie, either of the two branches above. */ + static struct varbuf fnvb; + + /* retry empty directories, and warn on any leftovers that aren't */ + removal_bulk_remove_leftover_dirs(pkg); + + varbufreset(&fnvb); + varbufaddstr(&fnvb,admindir); + varbufaddstr(&fnvb,"/" INFODIR); + varbufaddstr(&fnvb,pkg->name); + pkgnameused= fnvb.used; + + varbufaddstr(&fnvb,"." LISTFILE); + varbufaddc(&fnvb,0); + debug(dbg_general, "removal_bulk purge done, removing list `%s'",fnvb.buf); + if (unlink(fnvb.buf) && errno != ENOENT) ohshite(_("cannot remove old files list")); + + fnvb.used= pkgnameused; + varbufaddstr(&fnvb,"." POSTRMFILE); + varbufaddc(&fnvb,0); + debug(dbg_general, "removal_bulk purge done, removing postrm `%s'",fnvb.buf); + if (unlink(fnvb.buf) && errno != ENOENT) ohshite(_("can't remove old postrm script")); + + pkg->status= stat_notinstalled; + + /* This will mess up reverse links, but if we follow them + * we won't go back because pkg->status is stat_notinstalled. + */ + pkg->installed.depends= 0; + pkg->installed.essential= 0; + pkg->installed.description= pkg->installed.maintainer= 0; + pkg->installed.source= pkg->installed.installedsize= 0; + blankversion(&pkg->installed.version); + pkg->installed.arbs= 0; + } + + pkg->eflag= eflagv_ok; + modstatdb_note(pkg); + + debug(dbg_general, "removal done"); +} + diff --git a/src/select.c b/src/select.c new file mode 100644 index 000000000..a8becab28 --- /dev/null +++ b/src/select.c @@ -0,0 +1,146 @@ +/* + * dpkg - main program for package management + * select.c - by-hand (rather than dselect-based) package selection + * + * Copyright (C) 1995,1996 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <fnmatch.h> +#include <ctype.h> + +#include <dpkg.h> +#include <dpkg-db.h> + +#include "filesdb.h" +#include "main.h" + +static void getsel1package(struct pkginfo *pkg) { + int l; + + if (pkg->want == want_unknown) return; + l= strlen(pkg->name); l >>= 3; l= 6-l; if (l<1) l=1; + printf("%s%.*s%s\n",pkg->name,l,"\t\t\t\t\t\t",wantinfos[pkg->want].name); +} + +void getselections(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + struct pkginfo **pkgl; + const char *thisarg; + int np, i, head, found; + + modstatdb_init(admindir,msdbrw_readonly); + + np= countpackages(); + pkgl= m_malloc(sizeof(struct pkginfo*)*np); + it= iterpkgstart(); i=0; + while ((pkg= iterpkgnext(it))) { + assert(i<np); + pkgl[i++]= pkg; + } + iterpkgend(it); + assert(i==np); + + qsort(pkgl,np,sizeof(struct pkginfo*),pkglistqsortcmp); + head=0; + + if (!*argv) { + for (i=0; i<np; i++) { + pkg= pkgl[i]; + if (pkg->status == stat_notinstalled) continue; + getsel1package(pkg); + } + } else { + while ((thisarg= *argv++)) { + found= 0; + for (i=0; i<np; i++) { + pkg= pkgl[i]; + if (fnmatch(thisarg,pkg->name,0)) continue; + getsel1package(pkg); found++; + } + if (!found) + fprintf(stderr,_("No packages found matching %s.\n"),thisarg); + } + } + if (ferror(stdout)) werr("stdout"); + if (ferror(stderr)) werr("stderr"); +} + +void setselections(const char *const *argv) { + const struct namevalue *nvp; + struct pkginfo *pkg; + const char *e; + int c, lno; + struct varbuf namevb; + struct varbuf selvb; + + if (*argv) badusage(_("--set-selections does not take any argument")); + + modstatdb_init(admindir,msdbrw_write); + + varbufinit(&namevb); + varbufinit(&selvb); + lno= 1; + for (;;) { + varbufreset(&namevb); + varbufreset(&selvb); + do { c= getchar(); if (c == '\n') lno++; } while (c != EOF && isspace(c)); + if (c == EOF) break; + if (c == '#') { + do { c= getchar(); if (c == '\n') lno++; } while (c != EOF && c != '\n'); + continue; + } + while (!isspace(c)) { + varbufaddc(&namevb,c); + c= getchar(); + if (c == EOF) ohshit(_("unexpected eof in package name at line %d"),lno); + if (c == '\n') ohshit(_("unexpected end of line in package name at line %d"),lno); + } + while (c != EOF && isspace(c)) { + c= getchar(); + if (c == EOF) ohshit(_("unexpected eof after package name at line %d"),lno); + if (c == '\n') ohshit(_("unexpected end of line after package name at line %d"),lno); + } + while (c != EOF && !isspace(c)) { + varbufaddc(&selvb,c); + c= getchar(); + } + while (c != EOF && c != '\n') { + c= getchar(); + if (!isspace(c)) + ohshit(_("unexpected data after package and selection at line %d"),lno); + } + varbufaddc(&namevb,0); + varbufaddc(&selvb,0); + e= illegal_packagename(namevb.buf,0); + if (e) ohshit(_("illegal package name at line %d: %.250s"),lno,e); + for (nvp=wantinfos; nvp->name && strcmp(nvp->name,selvb.buf); nvp++); + if (!nvp->name) ohshit(_("unknown wanted status at line %d: %.250s"),lno,selvb.buf); + pkg= findpackage(namevb.buf); + pkg->want= nvp->value; + if (c == EOF) break; + lno++; + } + if (ferror(stdin)) ohshite(_("read error on standard input")); + modstatdb_shutdown(); + varbufreset(&namevb); + varbufreset(&selvb); +} diff --git a/src/update.c b/src/update.c new file mode 100644 index 000000000..14ba4eaf6 --- /dev/null +++ b/src/update.c @@ -0,0 +1,128 @@ +/* + * dpkg - main program for package management + * update.c - options which update the `available' database + * + * Copyright (C) 1995 Ian Jackson <ian@chiark.greenend.org.uk> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2, + * or (at your option) any later version. + * + * This is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with dpkg; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <config.h> + +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <fnmatch.h> +#include <assert.h> +#include <unistd.h> + +#include <dpkg.h> +#include <dpkg-db.h> +#include <myopt.h> + +#include "main.h" + +void updateavailable(const char *const *argv) { + const char *sourcefile= argv[0]; + int count= 0; + static struct varbuf vb; + + switch (cipaction->arg) { + case act_avclear: + if (sourcefile) badusage(_("--%s takes no arguments"),cipaction->olong); + break; + case act_avreplace: case act_avmerge: + if (!sourcefile || argv[1]) + badusage(_("--%s needs exactly one Packages file argument"),cipaction->olong); + break; + default: + internerr("bad cipaction->arg in updateavailable"); + } + + if (!f_noact) { + if (access(admindir,W_OK)) { + if (errno != EACCES) + ohshite(_("unable to access dpkg status area for bulk available update")); + else + ohshit(_("bulk available update requires write access to dpkg status area")); + } + lockdatabase(admindir); + } + + switch (cipaction->arg) { + case act_avreplace: + printf(_("Replacing available packages info, using %s.\n"),sourcefile); + break; + case act_avmerge: + printf(_("Updating available packages info, using %s.\n"),sourcefile); + break; + case act_avclear: + break; + default: + internerr("bad cipaction->arg in update available"); + } + + varbufaddstr(&vb,admindir); + varbufaddstr(&vb,"/" AVAILFILE); + varbufaddc(&vb,0); + + if (cipaction->arg == act_avmerge) + parsedb(vb.buf, pdb_recordavailable|pdb_rejectstatus, 0,0,0); + + if (cipaction->arg != act_avclear) + count+= parsedb(sourcefile, pdb_recordavailable|pdb_rejectstatus, 0,0,0); + + if (!f_noact) { + writedb(vb.buf,1,0); + unlockdatabase(admindir); + } + + if (cipaction->arg != act_avclear) + printf(_("Information about %d package(s) was updated.\n"),count); +} + +void forgetold(const char *const *argv) { + struct pkgiterator *it; + struct pkginfo *pkg; + enum pkgwant oldwant; + + if (*argv) badusage(_("--forget-old-unavail takes no arguments")); + + modstatdb_init(admindir, f_noact ? msdbrw_readonly : msdbrw_write); + + it= iterpkgstart(); + while ((pkg= iterpkgnext(it))) { + debug(dbg_eachfile,"forgetold checking %s",pkg->name); + if (informative(pkg,&pkg->available)) { + debug(dbg_eachfile,"forgetold ... informative available"); + continue; + } + if (pkg->want != want_purge && pkg->want != want_deinstall) { + debug(dbg_eachfile,"forgetold ... informative want"); + continue; + } + oldwant= pkg->want; + pkg->want= want_unknown; + if (informative(pkg,&pkg->installed)) { + debug(dbg_eachfile,"forgetold ... informative installed"); + pkg->want= oldwant; + continue; + } + debug(dbg_general,"forgetold forgetting %s",pkg->name); + } + iterpkgend(it); + + modstatdb_shutdown(); +} |