12 #include <sys/types.h>
25 static char * SCRIPT_PATH = "PATH=/sbin:/bin:/usr/sbin:/usr/bin:"
26 "/usr/X11R6/bin\nexport PATH\n";
28 enum fileActions { REMOVE, BACKUP, KEEP };
30 static int sharedFileCmp(const void * one, const void * two);
31 static int handleSharedFiles(rpmdb db, int offset, char ** fileList,
32 char ** fileMd5List, int fileCount,
33 enum fileActions * fileActions);
34 static int removeFile(char * file, char state, unsigned int flags, char * md5,
35 short mode, enum fileActions action, char * rmmess,
36 int brokenMd5, int test);
38 static int sharedFileCmp(const void * one, const void * two) {
39 if (((struct sharedFile *) one)->secRecOffset <
40 ((struct sharedFile *) two)->secRecOffset)
42 else if (((struct sharedFile *) one)->secRecOffset ==
43 ((struct sharedFile *) two)->secRecOffset)
49 int findSharedFiles(rpmdb db, int offset, char ** fileList, int fileCount,
50 struct sharedFile ** listPtr, int * listCountPtr) {
52 struct sharedFile * list = NULL;
54 int itemsAllocated = 0;
58 list = malloc(sizeof(struct sharedFile) * itemsAllocated);
60 for (i = 0; i < fileCount; i++) {
61 if (!rpmdbFindByFile(db, fileList[i], &matches)) {
62 for (j = 0; j < matches.count; j++) {
63 if (matches.recs[j].recOffset != offset) {
64 if (itemsUsed == itemsAllocated) {
66 list = realloc(list, sizeof(struct sharedFile) *
69 list[itemsUsed].mainFileNumber = i;
70 list[itemsUsed].secRecOffset = matches.recs[j].recOffset;
71 list[itemsUsed].secFileNumber = matches.recs[j].fileNumber;
76 dbiFreeIndexRecord(matches);
81 qsort(list, itemsUsed, sizeof(struct sharedFile), sharedFileCmp);
83 *listCountPtr = itemsUsed;
93 static int handleSharedFiles(rpmdb db, int offset, char ** fileList,
94 char ** fileMd5List, int fileCount,
95 enum fileActions * fileActions) {
98 struct sharedFile * sharedList;
100 char * name, * version, * release;
102 char ** secFileMd5List, ** secFileList;
103 char * secFileStatesList;
108 if (findSharedFiles(db, offset, fileList, fileCount, &sharedList,
117 for (i = 0; i < sharedCount; i++) {
118 if (secOffset != sharedList[i].secRecOffset) {
121 free(secFileMd5List);
125 secOffset = sharedList[i].secRecOffset;
126 sech = rpmdbGetRecord(db, secOffset);
128 rpmError(RPMERR_DBCORRUPT,
129 _("cannot read header at %d for uninstall"), offset);
134 headerGetEntry(sech, RPMTAG_NAME, &type, (void **) &name,
136 headerGetEntry(sech, RPMTAG_VERSION, &type, (void **) &version,
138 headerGetEntry(sech, RPMTAG_RELEASE, &type, (void **) &release,
141 rpmMessage(RPMMESS_DEBUG,
142 _("package %s-%s-%s contain shared files\n"),
143 name, version, release);
145 if (!headerGetEntry(sech, RPMTAG_FILENAMES, &type,
146 (void **) &secFileList, &secFileCount)) {
147 rpmError(RPMERR_DBCORRUPT, "package %s contains no files",
154 headerGetEntry(sech, RPMTAG_FILESTATES, &type,
155 (void **) &secFileStatesList, &secFileCount);
156 headerGetEntry(sech, RPMTAG_FILEMD5S, &type,
157 (void **) &secFileMd5List, &secFileCount);
160 rpmMessage(RPMMESS_DEBUG, "file %s is shared\n",
161 fileList[sharedList[i].mainFileNumber]);
163 switch (secFileStatesList[sharedList[i].secFileNumber]) {
164 case RPMFILE_STATE_REPLACED:
165 rpmMessage(RPMMESS_DEBUG, " file has already been replaced\n");
168 case RPMFILE_STATE_NOTINSTALLED:
169 rpmMessage(RPMMESS_DEBUG, " file was never installed\n");
172 case RPMFILE_STATE_NETSHARED:
173 rpmMessage(RPMMESS_DEBUG, " file is netshared (so don't touch it)\n");
174 fileActions[sharedList[i].mainFileNumber] = KEEP;
177 case RPMFILE_STATE_NORMAL:
178 if (!strcmp(fileMd5List[sharedList[i].mainFileNumber],
179 secFileMd5List[sharedList[i].secFileNumber])) {
180 rpmMessage(RPMMESS_DEBUG, " file is truely shared - saving\n");
182 fileActions[sharedList[i].mainFileNumber] = KEEP;
189 free(secFileMd5List);
197 int rpmRemovePackage(char * prefix, rpmdb db, unsigned int offset, int flags) {
201 char * rmmess, * name, * version, * release;
202 char * fnbuffer = NULL;
204 int fnbuffersize = 0;
205 int prefixLength = strlen(prefix);
206 char ** fileList, ** fileMd5List;
208 uint_32 * fileFlagsList;
209 int_16 * fileModesList;
210 char * fileStatesList;
211 enum { REMOVE, BACKUP, KEEP } * fileActions;
214 h = rpmdbGetRecord(db, offset);
216 rpmError(RPMERR_DBCORRUPT, "cannot read header at %d for uninstall",
221 headerGetEntry(h, RPMTAG_NAME, &type, (void **) &name, &count);
222 headerGetEntry(h, RPMTAG_VERSION, &type, (void **) &version, &count);
223 headerGetEntry(h, RPMTAG_RELEASE, &type, (void **) &release, &count);
224 /* when we run scripts, we pass an argument which is the number of
225 versions of this package that will be installed when we are finished */
226 if (rpmdbFindPackage(db, name, &matches)) {
227 rpmError(RPMERR_DBCORRUPT, "cannot read packages named %s for uninstall",
232 scriptArg = matches.count - 1;
233 dbiFreeIndexRecord(matches);
235 if (flags & RPMUNINSTALL_TEST) {
236 rmmess = "would remove";
241 rpmMessage(RPMMESS_DEBUG, "running preuninstall script (if any)\n");
243 if (runScript(prefix, h, RPMTAG_PREUN, scriptArg,
244 flags & RPMUNINSTALL_NOSCRIPTS)) {
249 rpmMessage(RPMMESS_DEBUG, "%s files test = %d\n", rmmess, flags & RPMUNINSTALL_TEST);
250 if (headerGetEntry(h, RPMTAG_FILENAMES, &type, (void **) &fileList,
254 fnbuffer = alloca(fnbuffersize);
257 headerGetEntry(h, RPMTAG_FILESTATES, &type, (void **) &fileStatesList,
259 headerGetEntry(h, RPMTAG_FILEMD5S, &type, (void **) &fileMd5List,
261 headerGetEntry(h, RPMTAG_FILEFLAGS, &type, (void **) &fileFlagsList,
263 headerGetEntry(h, RPMTAG_FILEMODES, &type, (void **) &fileModesList,
266 fileActions = alloca(sizeof(*fileActions) * fileCount);
267 for (i = 0; i < fileCount; i++)
268 if (fileStatesList[i] == RPMFILE_STATE_NOTINSTALLED ||
269 fileStatesList[i] == RPMFILE_STATE_NETSHARED)
270 fileActions[i] = KEEP;
272 fileActions[i] = REMOVE;
274 handleSharedFiles(db, offset, fileList, fileMd5List, fileCount, fileActions);
276 /* go through the filelist backwards to help insure that rmdir()
278 for (i = fileCount - 1; i >= 0; i--) {
279 if (strcmp(prefix, "/")) {
280 if ((strlen(fileList[i]) + prefixLength + 1) > fnbuffersize) {
281 fnbuffersize = (strlen(fileList[i]) + prefixLength) * 2;
282 fnbuffer = alloca(fnbuffersize);
284 strcpy(fnbuffer, prefix);
285 strcat(fnbuffer, "/");
286 strcat(fnbuffer, fileList[i]);
288 fnbuffer = fileList[i];
291 removeFile(fnbuffer, fileStatesList[i], fileFlagsList[i],
292 fileMd5List[i], fileModesList[i], fileActions[i],
293 rmmess, !headerIsEntry(h, RPMTAG_RPMVERSION),
294 flags & RPMUNINSTALL_TEST);
301 rpmMessage(RPMMESS_DEBUG, "running postuninstall script (if any)\n");
302 runScript(prefix, h, RPMTAG_POSTUN, scriptArg, flags & RPMUNINSTALL_NOSCRIPTS);
306 rpmMessage(RPMMESS_DEBUG, "%s database entry\n", rmmess);
307 if (!(flags & RPMUNINSTALL_TEST))
308 rpmdbRemove(db, offset, 0);
313 int runScript(char * prefix, Header h, int tag, int arg, int norunScripts) {
318 int isdebug = rpmIsDebug();
322 char * installPrefix = NULL;
323 char * installPrefixEnv = NULL;
325 sprintf(upgradeArg, "%d", arg);
327 if (norunScripts) return 0;
329 if (headerGetEntry(h, tag, &type, (void **) &script, &count)) {
330 if (headerGetEntry(h, RPMTAG_INSTALLPREFIX, &type, (void **) &installPrefix,
332 installPrefixEnv = alloca(strlen(installPrefix) + 30);
333 strcpy(installPrefixEnv, "RPM_INSTALL_PREFIX=");
334 strcat(installPrefixEnv, installPrefix);
338 rpmMessage(RPMMESS_DEBUG, "script found - running from file %s\n", fn);
339 fd = open(fn, O_CREAT | O_RDWR);
340 if (!isdebug) unlink(fn);
342 rpmError(RPMERR_SCRIPT,
343 _("error creating file for (un)install script"));
346 write(fd, SCRIPT_PATH, strlen(SCRIPT_PATH));
347 write(fd, script, strlen(script));
349 /* run the script via /bin/sh - just feed the commands to the
351 if (!(child = fork())) {
352 if (installPrefixEnv) {
353 doputenv(installPrefixEnv);
356 lseek(fd, 0, SEEK_SET);
361 if (strcmp(prefix, "/")) {
362 rpmMessage(RPMMESS_DEBUG, "performing chroot(%s)\n", prefix);
368 execl("/bin/sh", "/bin/sh", "-xs", upgradeArg, NULL);
370 execl("/bin/sh", "/bin/sh", "-s", upgradeArg, NULL);
374 waitpid(child, &status, 0);
376 if (!WIFEXITED(status) || WEXITSTATUS(status)) {
377 rpmError(RPMERR_SCRIPT, _("execution of script failed"));
385 static int removeFile(char * file, char state, unsigned int flags, char * md5,
386 short mode, enum fileActions action, char * rmmess,
387 int brokenMd5, int test) {
393 case RPMFILE_STATE_REPLACED:
394 rpmMessage(RPMMESS_DEBUG, "%s has already been replaced\n", file);
397 case RPMFILE_STATE_NORMAL:
398 if ((action == REMOVE) && (flags & RPMFILE_CONFIG)) {
399 /* if it's a config file, we may not want to remove it */
400 rpmMessage(RPMMESS_DEBUG, "finding md5sum of %s\n", file);
402 rc = mdfileBroken(file, currentMd5);
404 rc = mdfile(file, currentMd5);
406 if (mdfile(file, currentMd5))
407 rpmMessage(RPMMESS_DEBUG,
408 " failed - assuming file removed\n");
410 if (strcmp(currentMd5, md5)) {
411 rpmMessage(RPMMESS_DEBUG, " file changed - will save\n");
414 rpmMessage(RPMMESS_DEBUG,
415 " file unchanged - will remove\n");
423 rpmMessage(RPMMESS_DEBUG, "keeping %s\n", file);
427 rpmMessage(RPMMESS_DEBUG, "saving %s as %s.rpmsave\n", file, file);
429 newfile = alloca(strlen(file) + 20);
430 strcpy(newfile, file);
431 strcat(newfile, ".rpmsave");
432 if (rename(file, newfile)) {
433 rpmError(RPMERR_RENAME, _("rename of %s to %s failed: %s"),
434 file, newfile, strerror(errno));
441 rpmMessage(RPMMESS_DEBUG, "%s - %s\n", file, rmmess);
445 if (errno == ENOTEMPTY)
446 rpmError(RPMERR_RMDIR,
447 _("cannot remove %s - directory not empty"),
450 rpmError(RPMERR_RMDIR, _("rmdir of %s failed: %s"),
451 file, strerror(errno));
458 rpmError(RPMERR_UNLINK, _("removal of %s failed: %s"),
459 file, strerror(errno));