home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 February / CHIP_2_98.iso / misc / src / rpm / lib / uninstall.c < prev    next >
C/C++ Source or Header  |  1997-09-17  |  13KB  |  505 lines

  1. #include "config.h"
  2. #include "miscfn.h"
  3.  
  4. #if HAVE_ALLOCA_H
  5. # include <alloca.h>
  6. #endif 
  7.  
  8. #include <errno.h>
  9. #include <fcntl.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <sys/time.h>
  13. #include <sys/resource.h>
  14. #include <sys/stat.h>
  15. #include <sys/types.h>
  16. #include <sys/wait.h>
  17. #include <unistd.h>
  18.  
  19. #include "install.h"
  20. #include "intl.h"
  21. #include "messages.h"
  22. #include "md5.h"
  23. #include "misc.h"
  24. #include "rpmdb.h"
  25. #include "rpmlib.h"
  26.  
  27. static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:"
  28.                              "/usr/X11R6/bin\nexport PATH\n";
  29.  
  30. enum fileActions { REMOVE, BACKUP, KEEP };
  31.  
  32. static int sharedFileCmp(const void * one, const void * two);
  33. static int handleSharedFiles(rpmdb db, int offset, char ** fileList, 
  34.                  char ** fileMd5List, int fileCount, 
  35.                  enum fileActions * fileActions);
  36. static int removeFile(char * file, char state, unsigned int flags, char * md5, 
  37.               short mode, enum fileActions action, char * rmmess, 
  38.               int brokenMd5, int test);
  39.  
  40. static int sharedFileCmp(const void * one, const void * two) {
  41.     if (((struct sharedFile *) one)->secRecOffset <
  42.     ((struct sharedFile *) two)->secRecOffset)
  43.     return -1;
  44.     else if (((struct sharedFile *) one)->secRecOffset ==
  45.     ((struct sharedFile *) two)->secRecOffset)
  46.     return 0;
  47.     else 
  48.     return 1;
  49. }
  50.  
  51. int findSharedFiles(rpmdb db, int offset, char ** fileList, int fileCount,
  52.             struct sharedFile ** listPtr, int * listCountPtr) {
  53.     int i, j;
  54.     struct sharedFile * list = NULL;
  55.     int itemsUsed = 0;
  56.     int itemsAllocated = 0;
  57.     dbiIndexSet matches;
  58.  
  59.     itemsAllocated = 5;
  60.     list = malloc(sizeof(struct sharedFile) * itemsAllocated);
  61.  
  62.     for (i = 0; i < fileCount; i++) {
  63.     if (!rpmdbFindByFile(db, fileList[i], &matches)) {
  64.         for (j = 0; j < matches.count; j++) {
  65.         if (matches.recs[j].recOffset != offset) {
  66.             if (itemsUsed == itemsAllocated) {
  67.             itemsAllocated += 10;
  68.             list = realloc(list, sizeof(struct sharedFile) * 
  69.                         itemsAllocated);
  70.             }
  71.             list[itemsUsed].mainFileNumber = i;
  72.             list[itemsUsed].secRecOffset = matches.recs[j].recOffset;
  73.             list[itemsUsed].secFileNumber = matches.recs[j].fileNumber;
  74.             itemsUsed++;
  75.         }
  76.         }
  77.  
  78.         dbiFreeIndexRecord(matches);
  79.     }
  80.     }
  81.  
  82.     if (itemsUsed) {
  83.     qsort(list, itemsUsed, sizeof(struct sharedFile), sharedFileCmp);
  84.     *listPtr = list;
  85.     *listCountPtr = itemsUsed;
  86.     } else {
  87.     free(list);
  88.     *listPtr = NULL;
  89.     *listCountPtr = 0;
  90.     }
  91.  
  92.     return 0;
  93. }
  94.  
  95. static int handleSharedFiles(rpmdb db, int offset, char ** fileList, 
  96.                  char ** fileMd5List, int fileCount, 
  97.                  enum fileActions * fileActions) {
  98.     Header sech = NULL;
  99.     int secOffset = 0;
  100.     struct sharedFile * sharedList;
  101.     int sharedCount;
  102.     char * name, * version, * release;
  103.     int secFileCount;
  104.     char ** secFileMd5List, ** secFileList;
  105.     char * secFileStatesList;
  106.     int type;
  107.     int i;
  108.     int rc = 0;
  109.  
  110.     if (findSharedFiles(db, offset, fileList, fileCount, &sharedList, 
  111.             &sharedCount)) {
  112.     return 1;
  113.     }
  114.  
  115.     if (!sharedCount) {
  116.     return 0;
  117.     }
  118.  
  119.     for (i = 0; i < sharedCount; i++) {
  120.     if (secOffset != sharedList[i].secRecOffset) {
  121.         if (secOffset) {
  122.         headerFree(sech);
  123.         free(secFileMd5List);
  124.         free(secFileList);
  125.         }
  126.  
  127.         secOffset = sharedList[i].secRecOffset;
  128.         sech = rpmdbGetRecord(db, secOffset);
  129.         if (!sech) {
  130.         rpmError(RPMERR_DBCORRUPT, 
  131.              _("cannot read header at %d for uninstall"), offset);
  132.         rc = 1;
  133.         break;
  134.         }
  135.  
  136.         headerGetEntry(sech, RPMTAG_NAME, &type, (void **) &name, 
  137.              &secFileCount);
  138.         headerGetEntry(sech, RPMTAG_VERSION, &type, (void **) &version, 
  139.              &secFileCount);
  140.         headerGetEntry(sech, RPMTAG_RELEASE, &type, (void **) &release, 
  141.              &secFileCount);
  142.  
  143.         rpmMessage(RPMMESS_DEBUG, 
  144.             _("package %s-%s-%s contain shared files\n"), 
  145.                 name, version, release);
  146.  
  147.         if (!headerGetEntry(sech, RPMTAG_FILENAMES, &type, 
  148.               (void **) &secFileList, &secFileCount)) {
  149.         rpmError(RPMERR_DBCORRUPT, "package %s contains no files",
  150.               name);
  151.         headerFree(sech);
  152.         rc = 1;
  153.         break;
  154.         }
  155.  
  156.         headerGetEntry(sech, RPMTAG_FILESTATES, &type, 
  157.              (void **) &secFileStatesList, &secFileCount);
  158.         headerGetEntry(sech, RPMTAG_FILEMD5S, &type, 
  159.              (void **) &secFileMd5List, &secFileCount);
  160.     }
  161.  
  162.     rpmMessage(RPMMESS_DEBUG, "file %s is shared\n",
  163.         fileList[sharedList[i].mainFileNumber]);
  164.     
  165.     switch (secFileStatesList[sharedList[i].secFileNumber]) {
  166.       case RPMFILE_STATE_REPLACED:
  167.         rpmMessage(RPMMESS_DEBUG, "     file has already been replaced\n");
  168.         break;
  169.  
  170.       case RPMFILE_STATE_NOTINSTALLED:
  171.         rpmMessage(RPMMESS_DEBUG, "     file was never installed\n");
  172.         break;
  173.     
  174.       case RPMFILE_STATE_NETSHARED:
  175.         rpmMessage(RPMMESS_DEBUG, "     file is netshared (so don't touch it)\n");
  176.         fileActions[sharedList[i].mainFileNumber] = KEEP;
  177.         break;
  178.     
  179.       case RPMFILE_STATE_NORMAL:
  180.         if (!strcmp(fileMd5List[sharedList[i].mainFileNumber],
  181.             secFileMd5List[sharedList[i].secFileNumber])) {
  182.         rpmMessage(RPMMESS_DEBUG, "    file is truely shared - saving\n");
  183.         }
  184.         fileActions[sharedList[i].mainFileNumber] = KEEP;
  185.         break;
  186.     }
  187.     }
  188.  
  189.     if (secOffset) {
  190.     headerFree(sech);
  191.     free(secFileMd5List);
  192.     free(secFileList);
  193.     }
  194.     free(sharedList);
  195.  
  196.     return rc;
  197. }
  198.  
  199. int rpmRemovePackage(char * prefix, rpmdb db, unsigned int offset, int flags) {
  200.     Header h;
  201.     int i;
  202.     int fileCount;
  203.     char * rmmess, * name, * version, * release;
  204.     char * fnbuffer = NULL;
  205.     dbiIndexSet matches;
  206.     int fnbuffersize = 0;
  207.     int prefixLength = strlen(prefix);
  208.     char ** fileList, ** fileMd5List;
  209.     int type, count;
  210.     uint_32 * fileFlagsList;
  211.     int_16 * fileModesList;
  212.     char * fileStatesList;
  213.     enum { REMOVE, BACKUP, KEEP } * fileActions;
  214.     int scriptArg;
  215.  
  216.     if (flags & RPMUNINSTALL_JUSTDB)
  217.     flags |= RPMUNINSTALL_NOSCRIPTS;
  218.  
  219.     h = rpmdbGetRecord(db, offset);
  220.     if (!h) {
  221.     rpmError(RPMERR_DBCORRUPT, "cannot read header at %d for uninstall",
  222.           offset);
  223.     return 1;
  224.     }
  225.  
  226.     headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name,  &count);
  227.     headerGetEntry(h, RPMTAG_VERSION, &type, (void **) &version,  &count);
  228.     headerGetEntry(h, RPMTAG_RELEASE, &type, (void **) &release,  &count);
  229.     /* when we run scripts, we pass an argument which is the number of 
  230.        versions of this package that will be installed when we are finished */
  231.     if (rpmdbFindPackage(db, name, &matches)) {
  232.     rpmError(RPMERR_DBCORRUPT, "cannot read packages named %s for uninstall",
  233.           name);
  234.     return 1;
  235.     }
  236.  
  237.     scriptArg = matches.count - 1;
  238.     dbiFreeIndexRecord(matches);
  239.  
  240.     if (flags & RPMUNINSTALL_TEST) {
  241.     rmmess = "would remove";
  242.     } else {
  243.     rmmess = "removing";
  244.     }
  245.  
  246.     rpmMessage(RPMMESS_DEBUG, "running preuninstall script (if any)\n");
  247.  
  248.     if (runScript(prefix, h, RPMTAG_PREUN, RPMTAG_PREUNPROG, scriptArg, 
  249.          flags & RPMUNINSTALL_NOSCRIPTS)) {
  250.     headerFree(h);
  251.     return 1;
  252.     }
  253.     
  254.     rpmMessage(RPMMESS_DEBUG, "%s files test = %d\n", rmmess, flags & RPMUNINSTALL_TEST);
  255.     if (!(flags & RPMUNINSTALL_JUSTDB) &&
  256.     headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList, 
  257.                    &fileCount)) {
  258.     if (prefix[0]) {
  259.         fnbuffersize = 1024;
  260.         fnbuffer = alloca(fnbuffersize);
  261.     }
  262.  
  263.     headerGetEntry(h, RPMTAG_FILESTATES, &type, (void **) &fileStatesList, 
  264.          &fileCount);
  265.     headerGetEntry(h, RPMTAG_FILEMD5S, &type, (void **) &fileMd5List, 
  266.          &fileCount);
  267.     headerGetEntry(h, RPMTAG_FILEFLAGS, &type, (void **) &fileFlagsList, 
  268.          &fileCount);
  269.     headerGetEntry(h, RPMTAG_FILEMODES, &type, (void **) &fileModesList, 
  270.          &fileCount);
  271.  
  272.     fileActions = alloca(sizeof(*fileActions) * fileCount);
  273.     for (i = 0; i < fileCount; i++) 
  274.         if (fileStatesList[i] == RPMFILE_STATE_NOTINSTALLED ||
  275.         fileStatesList[i] == RPMFILE_STATE_NETSHARED) 
  276.         fileActions[i] = KEEP;
  277.         else
  278.         fileActions[i] = REMOVE;
  279.  
  280.     handleSharedFiles(db, offset, fileList, fileMd5List, fileCount, fileActions);
  281.  
  282.     /* go through the filelist backwards to help insure that rmdir()
  283.        will work */
  284.     for (i = fileCount - 1; i >= 0; i--) {
  285.         if (strcmp(prefix, "/")) {
  286.         if ((strlen(fileList[i]) + prefixLength + 1) > fnbuffersize) {
  287.             fnbuffersize = (strlen(fileList[i]) + prefixLength) * 2;
  288.             fnbuffer = alloca(fnbuffersize);
  289.         }
  290.         strcpy(fnbuffer, prefix);
  291.         strcat(fnbuffer, "/");
  292.         strcat(fnbuffer, fileList[i]);
  293.         } else {
  294.         fnbuffer = fileList[i];
  295.         }
  296.  
  297.         removeFile(fnbuffer, fileStatesList[i], fileFlagsList[i],
  298.                fileMd5List[i], fileModesList[i], fileActions[i], 
  299.                rmmess, !headerIsEntry(h, RPMTAG_RPMVERSION),
  300.                flags & RPMUNINSTALL_TEST);
  301.     }
  302.  
  303.     free(fileList);
  304.     free(fileMd5List);
  305.     }
  306.  
  307.     rpmMessage(RPMMESS_DEBUG, "running postuninstall script (if any)\n");
  308.     runScript(prefix, h, RPMTAG_POSTUN, RPMTAG_POSTUNPROG, scriptArg, 
  309.         flags & RPMUNINSTALL_NOSCRIPTS);
  310.  
  311.     headerFree(h);
  312.  
  313.     rpmMessage(RPMMESS_DEBUG, "%s database entry\n", rmmess);
  314.     if (!(flags & RPMUNINSTALL_TEST))
  315.     rpmdbRemove(db, offset, 0);
  316.  
  317.     return 0;
  318. }
  319.  
  320. int runScript(char * prefix, Header h, int scriptTag, int progTag,
  321.           int arg, int norunScripts) {
  322.     char * script;
  323.     char * fn;
  324.     int fd = -1;
  325.     int isdebug = rpmIsDebug();
  326.     int child;
  327.     int status;
  328.     char upgradeArg[20];
  329.     char * installPrefix = NULL;
  330.     char * installPrefixEnv = NULL;
  331.     char * argv[10];
  332.     char ** argvPtr = argv;
  333.     char * program;
  334.     int pipes[2];
  335.  
  336.     sprintf(upgradeArg, "%d", arg);
  337.  
  338.     if (norunScripts) return 0;
  339.  
  340.     /* headerGetEntry() sets the data pointer to NULL if the entry doesn
  341.        not exist */
  342.     headerGetEntry(h, progTag, NULL, (void **) &program, NULL);
  343.     headerGetEntry(h, scriptTag, NULL, (void **) &script, NULL);
  344.  
  345.     if (!program && !script)
  346.     return 0;
  347.     else if (!program)
  348.     program = "/bin/sh";
  349.  
  350.     if (headerGetEntry(h, RPMTAG_INSTALLPREFIX, NULL, (void **) &installPrefix,
  351.                    NULL)) {
  352.     installPrefixEnv = alloca(strlen(installPrefix) + 30);
  353.     strcpy(installPrefixEnv, "RPM_INSTALL_PREFIX=");
  354.     strcat(installPrefixEnv, installPrefix);
  355.     strcat(installPrefixEnv, "\n");
  356.     }
  357.  
  358.     rpmMessage(RPMMESS_DEBUG, "running inst helper %s\n", program);
  359.  
  360.     if (script) {
  361.     if (makeTempFile(prefix, &fn, &fd))
  362.         return 1;
  363.     write(fd, "#!", 2);
  364.     write(fd, program, strlen(program));
  365.  
  366.  
  367.     write(fd, "\n", 1);
  368.  
  369.     if (isdebug && !strcmp(program, "/bin/sh")) 
  370.         write(fd, "set -x\n", 7);
  371.  
  372.     if (installPrefixEnv)
  373.         write(fd, installPrefixEnv, strlen(installPrefixEnv));
  374.  
  375.     write(fd, SCRIPT_PATH, strlen(SCRIPT_PATH));
  376.     write(fd, script, strlen(script));
  377.     close(fd);
  378.  
  379.     program = fn + strlen(prefix);
  380.     }
  381.  
  382.     *argvPtr++ = program;
  383.     if (script) {
  384.     *argvPtr++ = upgradeArg;
  385.     }
  386.     *argvPtr++ = NULL;
  387.     
  388.     if (!(child = fork())) {
  389.     /* make stdin inaccessible */
  390.     pipe(pipes);
  391.     close(pipes[1]);
  392.  
  393.     dup2(pipes[0], 0);
  394.  
  395.     if (strcmp(prefix, "/")) {
  396.         rpmMessage(RPMMESS_DEBUG, "performing chroot(%s)\n", prefix);
  397.         chroot(prefix);
  398.         chdir("/");
  399.     }
  400.  
  401.     execv(argv[0], argv);
  402.     _exit(-1);
  403.     }
  404.  
  405.     waitpid(child, &status, 0);
  406.  
  407.     if (script) {
  408.     if (!isdebug) unlink(fn);
  409.     free(fn);
  410.     }
  411.  
  412.     if (!WIFEXITED(status) || WEXITSTATUS(status)) {
  413.     rpmError(RPMERR_SCRIPT, _("execution of script failed"));
  414.     return 1;
  415.     }
  416.  
  417.     return 0;
  418. }
  419.  
  420. static int removeFile(char * file, char state, unsigned int flags, char * md5, 
  421.               short mode, enum fileActions action, char * rmmess, 
  422.               int brokenMd5, int test) {
  423.     char currentMd5[40];
  424.     int rc = 0;
  425.     char * newfile;
  426.     
  427.     switch (state) {
  428.       case RPMFILE_STATE_REPLACED:
  429.     rpmMessage(RPMMESS_DEBUG, "%s has already been replaced\n", file);
  430.     break;
  431.  
  432.       case RPMFILE_STATE_NORMAL:
  433.     if ((action == REMOVE) && (flags & RPMFILE_CONFIG)) {
  434.         /* if it's a config file, we may not want to remove it */
  435.         rpmMessage(RPMMESS_DEBUG, "finding md5sum of %s\n", file);
  436.         if (brokenMd5)
  437.         rc = mdfileBroken(file, currentMd5);
  438.         else
  439.         rc = mdfile(file, currentMd5);
  440.  
  441.         if (mdfile(file, currentMd5)) 
  442.         rpmMessage(RPMMESS_DEBUG, 
  443.                 "    failed - assuming file removed\n");
  444.         else {
  445.         if (strcmp(currentMd5, md5)) {
  446.             rpmMessage(RPMMESS_DEBUG, "    file changed - will save\n");
  447.             action = BACKUP;
  448.         } else {
  449.             rpmMessage(RPMMESS_DEBUG, 
  450.                 "    file unchanged - will remove\n");
  451.         }
  452.         }
  453.     }
  454.  
  455.     switch (action) {
  456.  
  457.       case KEEP:
  458.         rpmMessage(RPMMESS_DEBUG, "keeping %s\n", file);
  459.         break;
  460.  
  461.       case BACKUP:
  462.         rpmMessage(RPMMESS_DEBUG, "saving %s as %s.rpmsave\n", file, file);
  463.         if (!test) {
  464.         newfile = alloca(strlen(file) + 20);
  465.         strcpy(newfile, file);
  466.         strcat(newfile, ".rpmsave");
  467.         if (rename(file, newfile)) {
  468.             rpmError(RPMERR_RENAME, _("rename of %s to %s failed: %s"),
  469.                 file, newfile, strerror(errno));
  470.             rc = 1;
  471.         }
  472.         }
  473.         break;
  474.  
  475.       case REMOVE:
  476.         rpmMessage(RPMMESS_DEBUG, "%s - %s\n", file, rmmess);
  477.         if (S_ISDIR(mode)) {
  478.         if (!test) {
  479.             if (rmdir(file)) {
  480.             if (errno == ENOTEMPTY)
  481.                 rpmError(RPMERR_RMDIR, 
  482.                 _("cannot remove %s - directory not empty"), 
  483.                 file);
  484.             else
  485.                 rpmError(RPMERR_RMDIR, _("rmdir of %s failed: %s"),
  486.                     file, strerror(errno));
  487.             rc = 1;
  488.             }
  489.         }
  490.         } else {
  491.         if (!test) {
  492.             if (unlink(file)) {
  493.             rpmError(RPMERR_UNLINK, _("removal of %s failed: %s"),
  494.                     file, strerror(errno));
  495.             rc = 1;
  496.             }
  497.         }
  498.         }
  499.         break;
  500.     }
  501.    }
  502.  
  503.    return 0;
  504. }
  505.