Main Page   Modules   Compound List   File List   Compound Members   File Members   Related Pages  

lib/uninstall.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 #include <rpmlib.h>
00008 #include <rpmurl.h>
00009 #include <rpmmacro.h>   /* XXX for rpmExpand */
00010 
00011 #include "depends.h"    /* XXX for headerMatchesDepFlags */
00012 #include "install.h"
00013 #include "misc.h"       /* XXX for makeTempFile, doputenv */
00014 #include "debug.h"
00015 
00016 /*@access Header@*/             /* XXX compared with NULL */
00017 
00018 static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/X11R6/bin";
00019 
00020 #define SUFFIX_RPMSAVE  ".rpmsave"
00021 
00027 static const char * tag2sln(int tag)
00028 {
00029     switch (tag) {
00030     case RPMTAG_PREIN:          return "%pre";
00031     case RPMTAG_POSTIN:         return "%post";
00032     case RPMTAG_PREUN:          return "%preun";
00033     case RPMTAG_POSTUN:         return "%postun";
00034     case RPMTAG_VERIFYSCRIPT:   return "%verify";
00035     }
00036     return "%unknownscript";
00037 }
00038 
00047 static int removeFile(const char * file, rpmfileAttrs fileAttrs, short mode, 
00048                       enum fileActions action)
00049 {
00050     int rc = 0;
00051     char * newfile;
00052         
00053     switch (action) {
00054 
00055       case FA_BACKUP:
00056         newfile = alloca(strlen(file) + sizeof(SUFFIX_RPMSAVE));
00057         (void)stpcpy(stpcpy(newfile, file), SUFFIX_RPMSAVE);
00058 
00059         if (rename(file, newfile)) {
00060             rpmError(RPMERR_RENAME, _("rename of %s to %s failed: %s\n"),
00061                         file, newfile, strerror(errno));
00062             rc = 1;
00063         }
00064         break;
00065 
00066       case FA_REMOVE:
00067         if (S_ISDIR(mode)) {
00068             /* XXX should permit %missingok for directories as well */
00069             if (rmdir(file)) {
00070                 switch (errno) {
00071                 case ENOENT:    /* XXX rmdir("/") linux 2.2.x kernel hack */
00072                 case ENOTEMPTY:
00073                     rpmError(RPMERR_RMDIR, 
00074                         _("cannot remove %s - directory not empty\n"), 
00075                         file);
00076                     break;
00077                 default:
00078                     rpmError(RPMERR_RMDIR, _("rmdir of %s failed: %s\n"),
00079                                 file, strerror(errno));
00080                     break;
00081                 }
00082                 rc = 1;
00083             }
00084         } else {
00085             if (unlink(file)) {
00086                 if (errno != ENOENT || !(fileAttrs & RPMFILE_MISSINGOK)) {
00087                     rpmError(RPMERR_UNLINK, 
00088                               _("removal of %s failed: %s\n"),
00089                                 file, strerror(errno));
00090                 }
00091                 rc = 1;
00092             }
00093         }
00094         break;
00095       case FA_UNKNOWN:
00096       case FA_CREATE:
00097       case FA_SAVE:
00098       case FA_ALTNAME:
00099       case FA_SKIP:
00100       case FA_SKIPNSTATE:
00101       case FA_SKIPNETSHARED:
00102       case FA_SKIPMULTILIB:
00103         break;
00104     }
00105  
00106     return 0;
00107 }
00108 
00109 int removeBinaryPackage(const rpmTransactionSet ts, unsigned int offset,
00110                 Header h, const void * pkgKey, enum fileActions * actions)
00111 {
00112     rpmtransFlags transFlags = ts->transFlags;
00113     const char * name, * version, * release;
00114     const char ** baseNames;
00115     int scriptArg;
00116     int rc = 0;
00117     int fileCount;
00118     int i;
00119 
00120     if (transFlags & RPMTRANS_FLAG_JUSTDB)
00121         transFlags |= RPMTRANS_FLAG_NOSCRIPTS;
00122 
00123     headerNVR(h, &name, &version, &release);
00124 
00125     /*
00126      * When we run scripts, we pass an argument which is the number of 
00127      * versions of this package that will be installed when we are finished.
00128      */
00129     if ((scriptArg = rpmdbCountPackages(ts->rpmdb, name)) < 0)
00130         return 1;
00131     scriptArg -= 1;
00132 
00133     if (!(transFlags & RPMTRANS_FLAG_NOTRIGGERS)) {
00134         /* run triggers from this package which are keyed on installed 
00135            packages */
00136         if (runImmedTriggers(ts, RPMSENSE_TRIGGERUN, h, -1))
00137             return 2;
00138 
00139         /* run triggers which are set off by the removal of this package */
00140         if (runTriggers(ts, RPMSENSE_TRIGGERUN, h, -1))
00141             return 1;
00142     }
00143 
00144     if (!(transFlags & RPMTRANS_FLAG_TEST)) {
00145         rc = runInstScript(ts, h, RPMTAG_PREUN, RPMTAG_PREUNPROG, scriptArg,
00146                           (transFlags & RPMTRANS_FLAG_NOSCRIPTS));
00147         if (rc)
00148             return 1;
00149     }
00150     
00151     rpmMessage(RPMMESS_DEBUG, _("will remove files test = %d\n"), 
00152                 transFlags & RPMTRANS_FLAG_TEST);
00153 
00154     if (!(transFlags & RPMTRANS_FLAG_JUSTDB) &&
00155         headerGetEntry(h, RPMTAG_BASENAMES, NULL, (void **) &baseNames, 
00156                        &fileCount)) {
00157         const char ** fileMd5List;
00158         uint_32 * fileFlagsList;
00159         int_16 * fileModesList;
00160         const char ** dirNames;
00161         int_32 * dirIndexes;
00162         int type;
00163         char * fileName;
00164         int fnmaxlen;
00165         int rdlen = (ts->rootDir && !(ts->rootDir[0] == '/' && ts->rootDir[1] == '\0'))
00166                         ? strlen(ts->rootDir) : 0;
00167 
00168         headerGetEntry(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
00169         headerGetEntry(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, NULL);
00170 
00171         /* Get buffer for largest possible rootDir + dirname + filename. */
00172         fnmaxlen = 0;
00173         for (i = 0; i < fileCount; i++) {
00174                 size_t fnlen;
00175                 fnlen = strlen(baseNames[i]) + 
00176                         strlen(dirNames[dirIndexes[i]]);
00177                 if (fnlen > fnmaxlen)
00178                     fnmaxlen = fnlen;
00179         }
00180         fnmaxlen += rdlen + sizeof("/");        /* XXX one byte too many */
00181 
00182         fileName = alloca(fnmaxlen);
00183 
00184         if (rdlen) {
00185             strcpy(fileName, ts->rootDir);
00186             (void)rpmCleanPath(fileName);
00187             rdlen = strlen(fileName);
00188         } else
00189             *fileName = '\0';
00190 
00191         headerGetEntry(h, RPMTAG_FILEMD5S, &type, (void **) &fileMd5List, 
00192                  &fileCount);
00193         headerGetEntry(h, RPMTAG_FILEFLAGS, &type, (void **) &fileFlagsList, 
00194                  &fileCount);
00195         headerGetEntry(h, RPMTAG_FILEMODES, &type, (void **) &fileModesList, 
00196                  &fileCount);
00197 
00198         if (ts->notify) {
00199             (void)ts->notify(h, RPMCALLBACK_UNINST_START, fileCount, fileCount,
00200                 pkgKey, ts->notifyData);
00201         }
00202 
00203         /* Traverse filelist backwards to help insure that rmdir() will work. */
00204         for (i = fileCount - 1; i >= 0; i--) {
00205 
00206             /* XXX this assumes that dirNames always starts/ends with '/' */
00207             (void)stpcpy(stpcpy(fileName+rdlen, dirNames[dirIndexes[i]]), baseNames[i]);
00208 
00209             rpmMessage(RPMMESS_DEBUG, _("   file: %s action: %s\n"),
00210                         fileName, fileActionString(actions[i]));
00211 
00212             if (!(transFlags & RPMTRANS_FLAG_TEST)) {
00213                 if (ts->notify) {
00214                     (void)ts->notify(h, RPMCALLBACK_UNINST_PROGRESS,
00215                         i, actions[i], fileName, ts->notifyData);
00216                 }
00217                 removeFile(fileName, fileFlagsList[i], fileModesList[i], 
00218                            actions[i]);
00219             }
00220         }
00221 
00222         if (ts->notify) {
00223             (void)ts->notify(h, RPMCALLBACK_UNINST_STOP, 0, fileCount,
00224                         pkgKey, ts->notifyData);
00225         }
00226 
00227         free(baseNames);
00228         free(dirNames);
00229         free(fileMd5List);
00230     }
00231 
00232     if (!(transFlags & RPMTRANS_FLAG_TEST)) {
00233         rpmMessage(RPMMESS_DEBUG, _("running postuninstall script (if any)\n"));
00234         rc = runInstScript(ts, h, RPMTAG_POSTUN, RPMTAG_POSTUNPROG,
00235                         scriptArg, (transFlags & RPMTRANS_FLAG_NOSCRIPTS));
00236         /* XXX postun failures are not cause for erasure failure. */
00237     }
00238 
00239     if (!(transFlags & RPMTRANS_FLAG_NOTRIGGERS)) {
00240         /* Run postun triggers which are set off by this package's removal. */
00241         rc = runTriggers(ts, RPMSENSE_TRIGGERPOSTUN, h, -1);
00242         if (rc)
00243             return 2;
00244     }
00245 
00246     if (!(transFlags & RPMTRANS_FLAG_TEST))
00247         rpmdbRemove(ts->rpmdb, ts->id, offset);
00248 
00249     return 0;
00250 }
00251 
00263 static int runScript(const rpmTransactionSet ts, Header h,
00264                 const char * sln,
00265                 int progArgc, const char ** progArgv, 
00266                 const char * script, int arg1, int arg2)
00267 {
00268     const char ** argv = NULL;
00269     int argc = 0;
00270     const char ** prefixes = NULL;
00271     int numPrefixes;
00272     const char * oldPrefix;
00273     int maxPrefixLength;
00274     int len;
00275     char * prefixBuf = NULL;
00276     pid_t child;
00277     int status = 0;
00278     const char * fn = NULL;
00279     int i;
00280     int freePrefixes = 0;
00281     FD_t out;
00282     int rc = 0;
00283     const char *n, *v, *r;
00284 
00285     if (!progArgv && !script)
00286         return 0;
00287     else if (!progArgv) {
00288         argv = alloca(5 * sizeof(char *));
00289         argv[0] = "/bin/sh";
00290         argc = 1;
00291     } else {
00292         argv = alloca((progArgc + 4) * sizeof(char *));
00293         memcpy(argv, progArgv, progArgc * sizeof(char *));
00294         argc = progArgc;
00295     }
00296 
00297     headerNVR(h, &n, &v, &r);
00298     if (headerGetEntry(h, RPMTAG_INSTPREFIXES, NULL, (void **) &prefixes,
00299                        &numPrefixes)) {
00300         freePrefixes = 1;
00301     } else if (headerGetEntry(h, RPMTAG_INSTALLPREFIX, NULL, 
00302                               (void **) &oldPrefix,
00303                        NULL)) {
00304         prefixes = &oldPrefix;
00305         numPrefixes = 1;
00306     } else {
00307         numPrefixes = 0;
00308     }
00309 
00310     maxPrefixLength = 0;
00311     for (i = 0; i < numPrefixes; i++) {
00312         len = strlen(prefixes[i]);
00313         if (len > maxPrefixLength) maxPrefixLength = len;
00314     }
00315     prefixBuf = alloca(maxPrefixLength + 50);
00316 
00317     if (script) {
00318         FD_t fd;
00319         if (makeTempFile((!ts->chrootDone ? ts->rootDir : "/"), &fn, &fd)) {
00320             if (freePrefixes) free(prefixes);
00321             return 1;
00322         }
00323 
00324         if (rpmIsDebug() &&
00325             (!strcmp(argv[0], "/bin/sh") || !strcmp(argv[0], "/bin/bash")))
00326             (void)Fwrite("set -x\n", sizeof(char), 7, fd);
00327 
00328         (void)Fwrite(script, sizeof(script[0]), strlen(script), fd);
00329         Fclose(fd);
00330 
00331         {   const char * sn = fn;
00332             if (!ts->chrootDone &&
00333                 !(ts->rootDir[0] == '/' && ts->rootDir[1] == '\0'))
00334             {
00335                 sn += strlen(ts->rootDir)-1;
00336             }
00337             argv[argc++] = sn;
00338         }
00339 
00340         if (arg1 >= 0) {
00341             char *av = alloca(20);
00342             sprintf(av, "%d", arg1);
00343             argv[argc++] = av;
00344         }
00345         if (arg2 >= 0) {
00346             char *av = alloca(20);
00347             sprintf(av, "%d", arg2);
00348             argv[argc++] = av;
00349         }
00350     }
00351 
00352     argv[argc] = NULL;
00353 
00354     if (ts->scriptFd != NULL) {
00355         if (rpmIsVerbose()) {
00356             out = fdDup(Fileno(ts->scriptFd));
00357         } else {
00358             out = Fopen("/dev/null", "w.fdio");
00359             if (Ferror(out)) {
00360                 out = fdDup(Fileno(ts->scriptFd));
00361             }
00362         }
00363     } else {
00364         out = fdDup(STDOUT_FILENO);
00365         out = fdLink(out, "runScript persist");
00366     }
00367     
00368     if (!(child = fork())) {
00369         const char * rootDir;
00370         int pipes[2];
00371 
00372         pipes[0] = pipes[1] = 0;
00373         /* make stdin inaccessible */
00374         pipe(pipes);
00375         close(pipes[1]);
00376         dup2(pipes[0], STDIN_FILENO);
00377         close(pipes[0]);
00378 
00379         if (ts->scriptFd != NULL) {
00380             if (Fileno(ts->scriptFd) != STDERR_FILENO)
00381                 dup2(Fileno(ts->scriptFd), STDERR_FILENO);
00382             if (Fileno(out) != STDOUT_FILENO)
00383                 dup2(Fileno(out), STDOUT_FILENO);
00384             /* make sure we don't close stdin/stderr/stdout by mistake! */
00385             if (Fileno(out) > STDERR_FILENO && Fileno(out) != Fileno(ts->scriptFd)) {
00386                 Fclose (out);
00387             }
00388             if (Fileno(ts->scriptFd) > STDERR_FILENO) {
00389                 Fclose (ts->scriptFd);
00390             }
00391         }
00392 
00393         {   const char *ipath = rpmExpand("PATH=%{_install_script_path}", NULL);
00394             const char *path = SCRIPT_PATH;
00395 
00396             if (ipath && ipath[5] != '%')
00397                 path = ipath;
00398             doputenv(path);
00399             if (ipath)  free((void *)ipath);
00400         }
00401 
00402         for (i = 0; i < numPrefixes; i++) {
00403             sprintf(prefixBuf, "RPM_INSTALL_PREFIX%d=%s", i, prefixes[i]);
00404             doputenv(prefixBuf);
00405 
00406             /* backwards compatibility */
00407             if (i == 0) {
00408                 sprintf(prefixBuf, "RPM_INSTALL_PREFIX=%s", prefixes[i]);
00409                 doputenv(prefixBuf);
00410             }
00411         }
00412 
00413         rootDir = ts->rootDir;
00414         switch(urlIsURL(rootDir)) {
00415         case URL_IS_PATH:
00416             rootDir += sizeof("file://") - 1;
00417             rootDir = strchr(rootDir, '/');
00418             /*@fallthrough@*/
00419         case URL_IS_UNKNOWN:
00420             if (!ts->chrootDone && !(rootDir[0] == '/' && rootDir[1] == '\0')) {
00421                 /*@-unrecog@*/ chroot(rootDir); /*@=unrecog@*/
00422             }
00423             chdir("/");
00424             execv(argv[0], (char *const *)argv);
00425             break;
00426         default:
00427             break;
00428         }
00429 
00430         _exit(-1);
00431         /*@notreached@*/
00432     }
00433 
00434     if (waitpid(child, &status, 0) < 0) {
00435         rpmError(RPMERR_SCRIPT,
00436                  _("retrieval of return code of %s scriptlet from %s-%s-%s failed, waitpid returned %s\n"),
00437                  sln, n, v, r, strerror (errno));
00438         /* XXX what to do here? */
00439         rc = 0;
00440     } else {
00441         if (!WIFEXITED(status) || WEXITSTATUS(status)) {
00442             rpmError(RPMERR_SCRIPT,
00443                      _("execution of %s scriptlet from %s-%s-%s failed, exit status %d\n"),
00444                      sln, n, v, r, WEXITSTATUS(status));
00445             rc = 1;
00446         }
00447     }
00448 
00449     if (freePrefixes) free(prefixes);
00450 
00451     Fclose(out);        /* XXX dup'd STDOUT_FILENO */
00452     
00453     if (script) {
00454         if (!rpmIsDebug()) unlink(fn);
00455         free((void *)fn);
00456     }
00457 
00458     return rc;
00459 }
00460 
00461 int runInstScript(const rpmTransactionSet ts, Header h,
00462                 int scriptTag, int progTag, int arg, int norunScripts)
00463 {
00464     void ** programArgv;
00465     int programArgc;
00466     const char ** argv;
00467     int programType;
00468     char * script;
00469     int rc;
00470 
00471     if (norunScripts) return 0;
00472 
00473     /* headerGetEntry() sets the data pointer to NULL if the entry does
00474        not exist */
00475     headerGetEntry(h, progTag, &programType, (void **) &programArgv,
00476                    &programArgc);
00477     headerGetEntry(h, scriptTag, NULL, (void **) &script, NULL);
00478 
00479     if (programArgv && programType == RPM_STRING_TYPE) {
00480         argv = alloca(sizeof(char *));
00481         *argv = (const char *) programArgv;
00482     } else {
00483         argv = (const char **) programArgv;
00484     }
00485 
00486     rc = runScript(ts, h, tag2sln(scriptTag), programArgc, argv, script,
00487                 arg, -1);
00488     headerFreeData(programArgv, programType);
00489     return rc;
00490 }
00491 
00502 static int handleOneTrigger(const rpmTransactionSet ts, int sense,
00503                         Header sourceH, Header triggeredH,
00504                         int arg1correction, int arg2,
00505                         char * triggersAlreadyRun)
00506 {
00507     const char ** triggerNames;
00508     const char ** triggerEVR;
00509     const char ** triggerScripts;
00510     const char ** triggerProgs;
00511     int_32 * triggerFlags;
00512     int_32 * triggerIndices;
00513     const char * triggerPackageName;
00514     const char * sourceName;
00515     int numTriggers;
00516     int rc = 0;
00517     int i;
00518     int skip;
00519 
00520     if (!headerGetEntry(triggeredH, RPMTAG_TRIGGERNAME, NULL, 
00521                         (void **) &triggerNames, &numTriggers)) {
00522         return 0;
00523     }
00524 
00525     headerNVR(sourceH, &sourceName, NULL, NULL);
00526 
00527     headerGetEntry(triggeredH, RPMTAG_TRIGGERFLAGS, NULL, 
00528                    (void **) &triggerFlags, NULL);
00529     headerGetEntry(triggeredH, RPMTAG_TRIGGERVERSION, NULL, 
00530                    (void **) &triggerEVR, NULL);
00531 
00532     for (i = 0; i < numTriggers; i++) {
00533 
00534         if (!(triggerFlags[i] & sense)) continue;
00535         if (strcmp(triggerNames[i], sourceName)) continue;
00536 
00537         /* For some reason, the TRIGGERVERSION stuff includes the name of the package which the trigger is based on. We need to skip
00538            over that here. I suspect that we'll change our minds on this
00539            and remove that, so I'm going to just 'do the right thing'. */
00540         skip = strlen(triggerNames[i]);
00541         if (!strncmp(triggerEVR[i], triggerNames[i], skip) &&
00542             (triggerEVR[i][skip] == '-'))
00543             skip++;
00544         else
00545             skip = 0;
00546 
00547         if (!headerMatchesDepFlags(sourceH, triggerNames[i],
00548                 triggerEVR[i] + skip, triggerFlags[i]))
00549             continue;
00550 
00551         headerGetEntry(triggeredH, RPMTAG_TRIGGERINDEX, NULL,
00552                        (void **) &triggerIndices, NULL);
00553         headerGetEntry(triggeredH, RPMTAG_TRIGGERSCRIPTS, NULL,
00554                        (void **) &triggerScripts, NULL);
00555         headerGetEntry(triggeredH, RPMTAG_TRIGGERSCRIPTPROG, NULL,
00556                        (void **) &triggerProgs, NULL);
00557 
00558         headerNVR(triggeredH, &triggerPackageName, NULL, NULL);
00559 
00560         {   int arg1;
00561             int index;
00562 
00563             if ((arg1 = rpmdbCountPackages(ts->rpmdb, triggerPackageName)) < 0) {
00564                 rc = 1; /* XXX W2DO? same as "execution of script failed" */
00565             } else {
00566                 arg1 += arg1correction;
00567                 index = triggerIndices[i];
00568                 if (!triggersAlreadyRun || !triggersAlreadyRun[index]) {
00569                     rc = runScript(ts, triggeredH, "%trigger", 1,
00570                             triggerProgs + index, triggerScripts[index], 
00571                             arg1, arg2);
00572                     if (triggersAlreadyRun) triggersAlreadyRun[index] = 1;
00573                 }
00574             }
00575         }
00576 
00577         free(triggerScripts);
00578         free(triggerProgs);
00579 
00580         /* each target/source header pair can only result in a single
00581            script being run */
00582         break;
00583     }
00584 
00585     free(triggerNames);
00586 
00587     return rc;
00588 }
00589 
00590 int runTriggers(const rpmTransactionSet ts, int sense, Header h,
00591                 int countCorrection)
00592 {
00593     const char * name;
00594     int numPackage;
00595     int rc = 0;
00596 
00597     headerNVR(h, &name, NULL, NULL);
00598 
00599     numPackage = rpmdbCountPackages(ts->rpmdb, name) + countCorrection;
00600     if (numPackage < 0)
00601         return 1;
00602 
00603     {   Header triggeredH;
00604         rpmdbMatchIterator mi;
00605 
00606         mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_TRIGGERNAME, name, 0);
00607         while((triggeredH = rpmdbNextIterator(mi)) != NULL) {
00608             rc |= handleOneTrigger(ts, sense, h, triggeredH, 0, numPackage, 
00609                                NULL);
00610         }
00611 
00612         rpmdbFreeIterator(mi);
00613     }
00614 
00615     return rc;
00616 }
00617 
00618 int runImmedTriggers(const rpmTransactionSet ts, int sense, Header h,
00619                      int countCorrection)
00620 {
00621     const char ** triggerNames;
00622     int numTriggers;
00623     int_32 * triggerIndices;
00624     int numTriggerIndices;
00625     char * triggersRun;
00626     int rc = 0;
00627 
00628     if (!headerGetEntry(h, RPMTAG_TRIGGERNAME, NULL, (void **) &triggerNames, 
00629                         &numTriggers))
00630         return 0;
00631     headerGetEntry(h, RPMTAG_TRIGGERINDEX, NULL, (void **) &triggerIndices, 
00632                    &numTriggerIndices);
00633     triggersRun = alloca(sizeof(*triggersRun) * numTriggerIndices);
00634     memset(triggersRun, 0, sizeof(*triggersRun) * numTriggerIndices);
00635 
00636     {   Header sourceH = NULL;
00637         int i;
00638 
00639         for (i = 0; i < numTriggers; i++) {
00640             rpmdbMatchIterator mi;
00641             const char * name = triggerNames[i];
00642 
00643             if (triggersRun[triggerIndices[i]]) continue;
00644         
00645             mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, name, 0);
00646 
00647             while((sourceH = rpmdbNextIterator(mi)) != NULL) {
00648                 rc |= handleOneTrigger(ts, sense, sourceH, h, 
00649                                    countCorrection, rpmdbGetIteratorCount(mi),
00650                                    triggersRun);
00651             }
00652 
00653             rpmdbFreeIterator(mi);
00654         }
00655     }
00656     return rc;
00657 }

Generated at Sun Apr 8 18:43:01 2001 for rpm by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000