summaryrefslogtreecommitdiff
path: root/main/help.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/help.c')
-rw-r--r--main/help.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/main/help.c b/main/help.c
new file mode 100644
index 000000000..48378a6c2
--- /dev/null
+++ b/main/help.c
@@ -0,0 +1,476 @@
+/*
+ * dpkg - main program for package management
+ * help.c - various helper routines
+ *
+ * Copyright (C) 1995 Ian Jackson <iwj10@cus.cam.ac.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 <unistd.h>
+#include <dirent.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "config.h"
+#include "dpkg.h"
+#include "dpkg-db.h"
+#include "myopt.h"
+
+#include "filesdb.h"
+#include "main.h"
+
+const char *const statusstrings[]= {
+ "not installed", "unpacked but not configured",
+ "broken due to postinst failure",
+ "installed",
+ "broken due to failed removal",
+ "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) {
+ static const char *const checklist[]= {
+ "ldconfig", "start-stop-daemon", "install-info", "update-rc.d", 0
+ };
+
+ struct stat stab;
+ const char *const *clp;
+ const char *path, *s, *p;
+ char buf[PATH_MAX+2];
+ int warned= 0;
+ long l;
+
+ path= getenv("PATH");
+ if (!path) fputs(DPKG " - warning: PATH is not set.\n", stderr);
+
+ for (clp=checklist; *clp; clp++) {
+ s= path;
+ while (s) {
+ p= strchr(s,':');
+ l= p ? p-s : strlen(s);
+ if (l+strlen(*clp)+2>sizeof(buf)) continue;
+ 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++;
+ }
+ }
+
+ 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 pkginfoperfile));
+ 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 void preexecscript(const char *path, char *const *argv) {
+ if (*instdir) {
+ /* fixme: won't work right when instdir != admindir */
+ 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);
+ }
+}
+
+static char *const *vbuildarglist(const char *scriptname, va_list ap) {
+ static char *bufs[PKGSCRIPTMAXARGS+1];
+ char *nextarg;
+ int i;
+
+ i=0;
+ bufs[i++]= (char*)scriptname; /* yes, cast away cost 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 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);
+}
+
+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;
+ int c1;
+ 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);
+ }
+ setexecute(scriptpath,&stab);
+ c1= m_fork();
+ if (!c1) {
+ preexecscript(scriptpath,arglist);
+ execv(scriptpath,arglist);
+ ohshite("unable to execute %s",buf);
+ }
+ script_catchsignals(); /* This does a push_cleanup() */
+ waitsubproc(c1,buf,0);
+ pop_cleanup(ehflag_normaltidy);
+
+ ensure_diversions();
+ return 1;
+}
+
+int maintainer_script_new(const char *scriptname, const char *description,
+ const char *cidir, char *cidirrest, ...) {
+ char *const *arglist;
+ struct stat stab;
+ va_list ap;
+ char buf[100];
+ int c1;
+
+ 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);
+ }
+ setexecute(cidir,&stab);
+ c1= m_fork();
+ if (!c1) {
+ preexecscript(cidir,arglist);
+ execv(cidir,arglist);
+ ohshite("unable to execute new %s",buf);
+ }
+ script_catchsignals(); /* This does a push_cleanup() */
+ waitsubproc(c1,buf,0);
+ pop_cleanup(ehflag_normaltidy);
+
+ 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;
+ int c1, n, status;
+ char buf[100];
+ pid_t r;
+
+ oldscriptpath= pkgadminfile(pkg,scriptname);
+ arglist= buildarglist(scriptname,
+ ifok,versiondescribe(pkg->available.version,
+ pkg->available.revision),
+ (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 {
+ setexecute(oldscriptpath,&stab);
+ c1= m_fork();
+ if (!c1) {
+ preexecscript(oldscriptpath,arglist);
+ execv(oldscriptpath,arglist);
+ ohshite("unable to execute %s",buf);
+ }
+ script_catchsignals(); /* This does a push_cleanup() */
+ while ((r= waitpid(c1,&status,0)) == -1 && errno == EINTR);
+ if (r != c1) ohshite("wait for %s failed",buf);
+ pop_cleanup(ehflag_normaltidy);
+ if (WIFEXITED(status)) {
+ n= WEXITSTATUS(status); if (!n) return 1;
+ fprintf(stderr, DPKG ": warning - %s returned error exit status %d\n",buf,n);
+ } else if (WIFSIGNALED(status)) {
+ n= WTERMSIG(status);
+ fprintf(stderr, DPKG ": warning - %s killed by signal (%s)%s\n",
+ buf, strsignal(n), WCOREDUMP(status) ? ", core dumped" : "");
+ } else {
+ ohshit("%s failed with unknown wait status code %d",buf,status);
+ }
+ ensure_diversions();
+ }
+ fprintf(stderr, DPKG " - trying script from the new package instead ...\n");
+
+ arglist= buildarglist(scriptname,
+ iffallback,versiondescribe(pkg->installed.version,
+ pkg->installed.revision),
+ (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);
+
+ setexecute(cidir,&stab);
+
+ c1= m_fork();
+ if (!c1) {
+ preexecscript(cidir,arglist);
+ execv(cidir,arglist);
+ ohshite("unable to execute %s",buf);
+ }
+ script_catchsignals(); /* This does a push_cleanup() */
+ waitsubproc(c1,buf,0);
+ pop_cleanup(ehflag_normaltidy);
+ 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(struct conffile *searchconff) {
+ struct filenamenode *namenode;
+
+ while (searchconff) {
+ namenode= findnamenode(searchconff->name);
+ namenode->flags |= fnnf_old_conff;
+ debug(dbg_conffdetail, "oldconffsetflags `%s' namenode %p flags %o",
+ searchconff->name, namenode, namenode->flags);
+ searchconff= searchconff->next;
+ }
+}
+
+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 (!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);
+}