12 #include <sys/types.h>
24 static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:"
25 "/usr/X11R6/bin\nexport PATH\n";
27 enum fileActions { REMOVE, BACKUP, KEEP };
29 static int sharedFileCmp(const void * one, const void * two);
30 static int handleSharedFiles(rpmdb db, int offset, char ** fileList,
31 char ** fileMd5List, int fileCount,
32 enum fileActions * fileActions);
33 static int removeFile(char * file, char state, unsigned int flags, char * md5,
34 short mode, enum fileActions action, char * rmmess,
35 int brokenMd5, int test);
37 static int sharedFileCmp(const void * one, const void * two) {
38 if (((struct sharedFile *) one)->secRecOffset <
39 ((struct sharedFile *) two)->secRecOffset)
41 else if (((struct sharedFile *) one)->secRecOffset ==
42 ((struct sharedFile *) two)->secRecOffset)
48 int findSharedFiles(rpmdb db, int offset, char ** fileList, int fileCount,
49 struct sharedFile ** listPtr, int * listCountPtr) {
51 struct sharedFile * list = NULL;
53 int itemsAllocated = 0;
57 list = malloc(sizeof(struct sharedFile) * itemsAllocated);
59 for (i = 0; i < fileCount; i++) {
60 if (!rpmdbFindByFile(db, fileList[i], &matches)) {
61 for (j = 0; j < matches.count; j++) {
62 if (matches.recs[j].recOffset != offset) {
63 if (itemsUsed == itemsAllocated) {
65 list = realloc(list, sizeof(struct sharedFile) *
68 list[itemsUsed].mainFileNumber = i;
69 list[itemsUsed].secRecOffset = matches.recs[j].recOffset;
70 list[itemsUsed].secFileNumber = matches.recs[j].fileNumber;
75 dbiFreeIndexRecord(matches);
80 qsort(list, itemsUsed, sizeof(struct sharedFile), sharedFileCmp);
82 *listCountPtr = itemsUsed;
92 static int handleSharedFiles(rpmdb db, int offset, char ** fileList,
93 char ** fileMd5List, int fileCount,
94 enum fileActions * fileActions) {
97 struct sharedFile * sharedList;
99 char * name, * version, * release;
101 char ** secFileMd5List, ** secFileList;
102 char * secFileStatesList;
107 if (findSharedFiles(db, offset, fileList, fileCount, &sharedList,
116 for (i = 0; i < sharedCount; i++) {
117 if (secOffset != sharedList[i].secRecOffset) {
120 free(secFileMd5List);
124 secOffset = sharedList[i].secRecOffset;
125 sech = rpmdbGetRecord(db, secOffset);
127 rpmError(RPMERR_DBCORRUPT,
128 _("cannot read header at %d for uninstall"), offset);
133 headerGetEntry(sech, RPMTAG_NAME, &type, (void **) &name,
135 headerGetEntry(sech, RPMTAG_VERSION, &type, (void **) &version,
137 headerGetEntry(sech, RPMTAG_RELEASE, &type, (void **) &release,
140 rpmMessage(RPMMESS_DEBUG,
141 _("package %s-%s-%s contain shared files\n"),
142 name, version, release);
144 if (!headerGetEntry(sech, RPMTAG_FILENAMES, &type,
145 (void **) &secFileList, &secFileCount)) {
146 rpmError(RPMERR_DBCORRUPT, "package %s contains no files",
153 headerGetEntry(sech, RPMTAG_FILESTATES, &type,
154 (void **) &secFileStatesList, &secFileCount);
155 headerGetEntry(sech, RPMTAG_FILEMD5S, &type,
156 (void **) &secFileMd5List, &secFileCount);
159 rpmMessage(RPMMESS_DEBUG, "file %s is shared\n",
160 fileList[sharedList[i].mainFileNumber]);
162 switch (secFileStatesList[sharedList[i].secFileNumber]) {
163 case RPMFILE_STATE_REPLACED:
164 rpmMessage(RPMMESS_DEBUG, " file has already been replaced\n");
167 case RPMFILE_STATE_NOTINSTALLED:
168 rpmMessage(RPMMESS_DEBUG, " file was never installed\n");
171 case RPMFILE_STATE_NETSHARED:
172 rpmMessage(RPMMESS_DEBUG, " file is netshared (so don't touch it)\n");
173 fileActions[sharedList[i].mainFileNumber] = KEEP;
176 case RPMFILE_STATE_NORMAL:
177 if (!strcmp(fileMd5List[sharedList[i].mainFileNumber],
178 secFileMd5List[sharedList[i].secFileNumber])) {
179 rpmMessage(RPMMESS_DEBUG, " file is truely shared - saving\n");
181 fileActions[sharedList[i].mainFileNumber] = KEEP;
188 free(secFileMd5List);
196 int rpmRemovePackage(char * prefix, rpmdb db, unsigned int offset, int flags) {
200 char * rmmess, * name, * version, * release;
201 char * fnbuffer = NULL;
203 int fnbuffersize = 0;
204 int prefixLength = strlen(prefix);
205 char ** fileList, ** fileMd5List;
207 uint_32 * fileFlagsList;
208 int_16 * fileModesList;
209 char * fileStatesList;
210 enum { REMOVE, BACKUP, KEEP } * fileActions;
213 h = rpmdbGetRecord(db, offset);
215 rpmError(RPMERR_DBCORRUPT, "cannot read header at %d for uninstall",
220 headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count);
221 headerGetEntry(h, RPMTAG_VERSION, &type, (void **) &version, &count);
222 headerGetEntry(h, RPMTAG_RELEASE, &type, (void **) &release, &count);
223 /* when we run scripts, we pass an argument which is the number of
224 versions of this package that will be installed when we are finished */
225 if (rpmdbFindPackage(db, name, &matches)) {
226 rpmError(RPMERR_DBCORRUPT, "cannot read packages named %s for uninstall",
231 scriptArg = matches.count - 1;
232 dbiFreeIndexRecord(matches);
234 if (flags & RPMUNINSTALL_TEST) {
235 rmmess = "would remove";
240 rpmMessage(RPMMESS_DEBUG, "running preuninstall script (if any)\n");
242 if (runScript(prefix, h, RPMTAG_PREUN, scriptArg,
243 flags & RPMUNINSTALL_NOSCRIPTS)) {
248 rpmMessage(RPMMESS_DEBUG, "%s files test = %d\n", rmmess, flags & RPMUNINSTALL_TEST);
249 if (headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList,
253 fnbuffer = alloca(fnbuffersize);
256 headerGetEntry(h, RPMTAG_FILESTATES, &type, (void **) &fileStatesList,
258 headerGetEntry(h, RPMTAG_FILEMD5S, &type, (void **) &fileMd5List,
260 headerGetEntry(h, RPMTAG_FILEFLAGS, &type, (void **) &fileFlagsList,
262 headerGetEntry(h, RPMTAG_FILEMODES, &type, (void **) &fileModesList,
265 fileActions = alloca(sizeof(*fileActions) * fileCount);
266 for (i = 0; i < fileCount; i++)
267 if (fileStatesList[i] == RPMFILE_STATE_NOTINSTALLED ||
268 fileStatesList[i] == RPMFILE_STATE_NETSHARED)
269 fileActions[i] = KEEP;
271 fileActions[i] = REMOVE;
273 handleSharedFiles(db, offset, fileList, fileMd5List, fileCount, fileActions);
275 /* go through the filelist backwards to help insure that rmdir()
277 for (i = fileCount - 1; i >= 0; i--) {
278 if (strcmp(prefix, "/")) {
279 if ((strlen(fileList[i]) + prefixLength + 1) > fnbuffersize) {
280 fnbuffersize = (strlen(fileList[i]) + prefixLength) * 2;
281 fnbuffer = alloca(fnbuffersize);
283 strcpy(fnbuffer, prefix);
284 strcat(fnbuffer, "/");
285 strcat(fnbuffer, fileList[i]);
287 fnbuffer = fileList[i];
290 removeFile(fnbuffer, fileStatesList[i], fileFlagsList[i],
291 fileMd5List[i], fileModesList[i], fileActions[i],
292 rmmess, !headerIsEntry(h, RPMTAG_RPMVERSION),
293 flags & RPMUNINSTALL_TEST);
300 rpmMessage(RPMMESS_DEBUG, "running postuninstall script (if any)\n");
301 runScript(prefix, h, RPMTAG_POSTUN, scriptArg, flags & RPMUNINSTALL_NOSCRIPTS);
305 rpmMessage(RPMMESS_DEBUG, "%s database entry\n", rmmess);
306 if (!(flags & RPMUNINSTALL_TEST))
307 rpmdbRemove(db, offset, 0);
312 int runScript(char * prefix, Header h, int tag, int arg, int norunScripts) {
317 int isdebug = rpmIsDebug();
321 char * installPrefix = NULL;
322 char * installPrefixEnv = NULL;
324 sprintf(upgradeArg, "%d", arg);
326 if (norunScripts) return 0;
328 if (headerGetEntry(h, tag, &type, (void **) &script, &count)) {
329 if (headerGetEntry(h, RPMTAG_INSTALLPREFIX, &type, (void **) &installPrefix,
331 installPrefixEnv = alloca(strlen(installPrefix) + 30);
332 strcpy(installPrefixEnv, "RPM_INSTALL_PREFIX=");
333 strcat(installPrefixEnv, installPrefix);
337 rpmMessage(RPMMESS_DEBUG, "script found - running from file %s\n", fn);
338 fd = open(fn, O_CREAT | O_RDWR);
339 if (!isdebug) unlink(fn);
341 rpmError(RPMERR_SCRIPT,
342 _("error creating file for (un)install script"));
345 write(fd, SCRIPT_PATH, strlen(SCRIPT_PATH));
346 write(fd, script, strlen(script));
348 /* run the script via /bin/sh - just feed the commands to the
350 if (!(child = fork())) {
351 if (installPrefixEnv) {
352 doputenv(installPrefixEnv);
355 lseek(fd, 0, SEEK_SET);
360 if (strcmp(prefix, "/")) {
361 rpmMessage(RPMMESS_DEBUG, "performing chroot(%s)\n", prefix);
367 execl("/bin/sh", "/bin/sh", "-xs", upgradeArg, NULL);
369 execl("/bin/sh", "/bin/sh", "-s", upgradeArg, NULL);
373 waitpid(child, &status, 0);
375 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
376 rpmError(RPMERR_SCRIPT, _("execution of script failed"));
384 static int removeFile(char * file, char state, unsigned int flags, char * md5,
385 short mode, enum fileActions action, char * rmmess,
386 int brokenMd5, int test) {
392 case RPMFILE_STATE_REPLACED:
393 rpmMessage(RPMMESS_DEBUG, "%s has already been replaced\n", file);
396 case RPMFILE_STATE_NORMAL:
397 if ((action == REMOVE) && (flags & RPMFILE_CONFIG)) {
398 /* if it's a config file, we may not want to remove it */
399 rpmMessage(RPMMESS_DEBUG, "finding md5sum of %s\n", file);
401 rc = mdfileBroken(file, currentMd5);
403 rc = mdfile(file, currentMd5);
405 if (mdfile(file, currentMd5))
406 rpmMessage(RPMMESS_DEBUG,
407 " failed - assuming file removed\n");
409 if (strcmp(currentMd5, md5)) {
410 rpmMessage(RPMMESS_DEBUG, " file changed - will save\n");
413 rpmMessage(RPMMESS_DEBUG,
414 " file unchanged - will remove\n");
422 rpmMessage(RPMMESS_DEBUG, "keeping %s\n", file);
426 rpmMessage(RPMMESS_DEBUG, "saving %s as %s.rpmsave\n", file, file);
428 newfile = alloca(strlen(file) + 20);
429 strcpy(newfile, file);
430 strcat(newfile, ".rpmsave");
431 if (rename(file, newfile)) {
432 rpmError(RPMERR_RENAME, _("rename of %s to %s failed: %s"),
433 file, newfile, strerror(errno));
440 rpmMessage(RPMMESS_DEBUG, "%s - %s\n", file, rmmess);
444 if (errno == ENOTEMPTY)
445 rpmError(RPMERR_RMDIR,
446 _("cannot remove %s - directory not empty"),
449 rpmError(RPMERR_RMDIR, _("rmdir of %s failed: %s"),
450 file, strerror(errno));
457 rpmError(RPMERR_UNLINK, _("removal of %s failed: %s"),
458 file, strerror(errno));