A hint: This file contains one or more very long lines, so maybe it is better readable using the pure text view mode that shows the contents as wrapped lines within the browser window.
1 /******************************************************************************\ 2 * * 3 * UNIPKG (c) iSteve <isteve@bofh.cz>, 2005 * 4 * * 5 * Universal PacKaGer. * 6 * Licensed under GNU/GPL - if you don't like the software, fix it! * 7 * * 8 \******************************************************************************/ 9 10 /////////////////////////////////////////////////////////////////////////////// 11 // // 12 // B A S E I N C L U S I O N S A N D D E F I N I T I O N S // 13 // // 14 /////////////////////////////////////////////////////////////////////////////// 15 #define _GNU_SOURCE 16 17 #include <time.h> 18 #include <stdlib.h> 19 #include <stdio.h> 20 #include <stdarg.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <ctype.h> 24 #include <sys/stat.h> 25 #include <sys/types.h> 26 #include <sys/wait.h> 27 #include <signal.h> 28 #include <fnmatch.h> 29 #include <dlfcn.h> 30 31 #include "common.h" 32 #include "manmachine.h" 33 34 #define MAXATTEMPTS 1024 35 36 // Thou shalt ->NOT<- change this unless ->REALLY<- knowing what you are messing with! 37 #define DATAPREFIX "/" 38 // Specifies the default db used, if not specified otherwise in config. 39 #define DEFDB "libunipkg-directory.so.1" 40 // Specifies the default path to db used, if not specified otherwise in config. 41 #ifndef DBPATH 42 #define DBPATH "/var/lib/unipkg" 43 #endif 44 // This is the shell command to which scripts are piped 45 #define SHELL "/bin/sh > /dev/null 2> /dev/null" 46 #define REDIRECT " 2> /dev/null" 47 // This is the default config file 48 #ifndef CONFPATH 49 #define CONFPATH "/etc/unipkg.conf" 50 #endif 51 52 typedef struct { // Let this struct grow, it will later on contain full setup of unipkg. 53 char *dbpath; // database path 54 char *instdir; // directory to which data are installed 55 char *dblib; 56 int usescripts; // do we use the scripts? [-1 = undecided, 0 = no, 1 = yes] 57 int failsafe; // do we ignore failing scripts? [-1 = undecided, 0 = no, 1 = yes] 58 int ensurermdir; // do we want to ensure removing dirs? [0 = no, 1 = yes] 59 char **pkg_libraries; 60 char **protecteddirs; 61 } unipkgconfig; 62 63 /////////////////////////////////////////////////////////////////////////////// 64 // // 65 // B A S E F U N C T I O N S // 66 // // 67 /////////////////////////////////////////////////////////////////////////////// 68 69 // USAGE() 70 // 71 // Prints out the usage of unipkg. 72 // 73 // Parameters: 74 // none 75 // 76 // Return value: 77 // none 78 void Usage() { 79 fprintf(stderr, "unipkg Universal PacKaGer.\n\n"); 80 fprintf(stderr, "Usage: unipkg install|preinfo <filename>\n"); 81 fprintf(stderr, " unipkg remove|info|listfiles|find <pkg name>\n"); 82 fprintf(stderr, " unipkg findfile <filename>\n"); 83 fprintf(stderr, " unipkg execute <pkg name> <script>\n"); 84 fprintf(stderr, " unipkg list\n\n"); 85 fprintf(stderr, "unipkg install - Installs the file(s) to the system.\n"); 86 fprintf(stderr, "unipkg remove - Uninstalls specified package.\n"); 87 fprintf(stderr, "unipkg info - Prints info about the package. Returns only first hit (if there is a wildcard).\n"); 88 fprintf(stderr, "unipkg preinfo - Prints info about a package not yet installed.\n"); 89 fprintf(stderr, "unipkg list - Lists currently installed packages.\n"); 90 fprintf(stderr, "unipkg listfiles - Lists files in a package. Returns only first hit (if there is a wildcard).\n"); 91 fprintf(stderr, "unipkg find - Finds matching package(s). The wildcard '*' is appended and prepended to the needle.\n"); 92 fprintf(stderr, "unipkg findfile - Finds files and returns package names. The wildcard '*' is appended and prepended to the needle.\n\n"); 93 fprintf(stderr, "unipkg execute - Executes given script ('preinst', 'postinst', 'preremove', 'postremove') of given package.\n"); 94 fprintf(stderr, "NOTE: You can use wildcards (globs) such as '*' or '?' within the pkgname. That means, you can use eg. unipkg find *bar, and it will list any package that ends with 'bar'.\n"); 95 } 96 97 // ABSOLUTIZE() 98 // 99 // Turns all files in pkginfo into absolute, 100 // 101 // Parameters: 102 // pointer to pkginfo structure, whose members are to be absolutized 103 // string prefixing the paths in first parameter 104 // 105 // Return value: 106 // 0 if success 107 // 1 if error 108 // 2 if possible hacking attempt (weird directories, such as ~/, referring to home) 109 int absolutize(pkginfo *toabs, char *dirprefix) { 110 unsigned long i; 111 char *path, *prefix, *ptr; 112 int reval=0; 113 114 prefix = malloc(strlen(dirprefix) + 1); 115 prefix = strcpy(prefix, dirprefix); 116 117 if (prefix[strlen(prefix)-1] == '/') { prefix[strlen(prefix)-1] = 0x0; } 118 for (i=0; i<toabs->filecount; i++) { 119 if ((ptr = strstr(toabs->files[i].name, "~/")) != NULL) { 120 if (ptr == toabs->files[i].name) { // '~/' at start of filename - no way, baby! 121 reval=2; 122 } 123 else { 124 if (*(ptr-1) == '/') { reval=2; } // '~/' wasn't at start of filename, but doh, it was following a dirname - smeg off. 125 } 126 } 127 128 if ((ptr = strstr(toabs->files[i].name, "../")) != NULL) { 129 if (ptr == toabs->files[i].name) { // '../' at start of filename - no way, baby! 130 reval=2; 131 } 132 else { 133 if (*(ptr-1) == '/') { reval=2; } // '../' wasn't at start of filename, but doh, it was following a dirname - smeg off. 134 } 135 } 136 137 ptr = toabs->files[i].name; 138 if ((*ptr == '.') && (*(ptr+1) == '/')) { ptr+=2; } // It starts with './', which is to be skipped 139 while (*ptr == '/') { ptr++; } // Skip heading slashes, we added them ourselves 140 141 path = malloc(strlen(ptr) + strlen(prefix) + 2); 142 143 strcpy(path, prefix); 144 strcpy(path+strlen(prefix), "/"); 145 strcpy(path+strlen(prefix)+1, ptr); 146 147 if (toabs->files[i].name != NULL) { free(toabs->files[i].name); } // I know, I know, free(NULL) won't do anything, but ... 148 149 toabs->files[i].name = strdup(path); 150 free(path); 151 } 152 153 free(prefix); 154 155 return reval; 156 } 157 158 // ENSURERMDIR() 159 // 160 // Attempts to, sequentally, remove all dirs containing the parameter. It 161 // keeps the directories if they contain something (ie. if rmdir fails). 162 // 163 // Parameters: 164 // path inside directories which are going to be removed 165 // 166 // Return value: 167 // none 168 void ensurermdir(char *inpath) { 169 char *path, *slash; 170 171 path = strdup(inpath); 172 173 do { 174 slash=strrchr(path, '/'); 175 if (slash == NULL) { break; } 176 *slash=0x0; 177 } while (rmdir(path) == 0); 178 179 free(path); 180 181 return; 182 } 183 184 // DUPEXISTING() 185 // 186 // Dumps a new pkginfo containing files that exist in system. 187 // 188 // Parameters: 189 // pkginfo structure where to write 190 // pkginfo structure which is checked 191 // 192 // Return value: 193 // nothing 194 void dupexisting(pkginfo *rv, pkginfo toinst) { 195 unsigned long i; 196 struct stat statret; 197 198 for (i = 0; i < toinst.filecount; i++) { 199 if (lstat(toinst.files[i].name, &statret)) 200 continue; 201 202 addfile(rv, strdup(toinst.files[i].name), S_ISDIR(statret.st_mode) ? F_DIRECTORY : F_REGULAR); 203 } 204 205 // Those two are quite relevant; we may do comparsion based 206 // on version or name. 207 rv->name = strdup(toinst.name); 208 rv->version = strdup(toinst.version); 209 } 210 211 // SETFLAGS() 212 // 213 // Ensures that pkg has proper file flags set. Currently unused. 214 // 215 // Parameters: 216 // pkginfo structure source 217 // 218 // Return value: 219 // nothing 220 void setflags(pkginfo *flagged) { 221 unsigned long i; 222 223 for (i = 0; i < flagged->filecount; i++) { 224 if (flagged->files[i].flag == F_IRRELEVANT) 225 flagged->files[i].flag = fisdir(flagged->files[i].name) ? F_DIRECTORY : F_REGULAR; 226 } 227 } 228 229 // SUBSTRACTFILES() 230 // 231 // Substract files from a package. 232 // 233 // Parameters: 234 // pkginfo structure source 235 // pkginfo structure that substracts 236 // pkginfo structure target 237 // 238 // Return value: 239 // nothing 240 void substractfiles(pkginfo substracted, pkginfo substracting, pkginfo *rv) { 241 unsigned long i, j, add; 242 243 for (i = 0; i < substracted.filecount; i++) { 244 add = 1; 245 for (j = 0; j < substracting.filecount; j++) { 246 if (!strcmp(substracting.files[j].name, substracted.files[i].name)) { 247 add = 0; 248 break; 249 } 250 } 251 252 if (add) { 253 addfile(rv, strdup(substracted.files[i].name), substracted.files[i].flag); 254 } 255 } 256 257 // Those two are quite relevant; we may do comparsion based 258 // on version or name. 259 rv->name = strdup(substracting.name); 260 rv->version = strdup(substracting.version); 261 } 262 263 // PREINSTCHECK() 264 // 265 // Checks if there are any files which would collide with the passed package, 266 // if it was installed now. Also check if everything above the file is actually 267 // nonexistent or a directory. 268 // 269 // Parameters: 270 // pkginfo structure which is checked 271 // 272 // Return value: 273 // 0 if no collision 274 // 1 if collision 275 int preinstcheck(pkginfo toinst) { 276 unsigned long i; 277 struct stat statret; 278 char *ptr, *dup; 279 280 for (i=0; i<toinst.filecount; i++) { 281 if (!stat(toinst.files[i].name, &statret)) { 282 if ((S_ISDIR(statret.st_mode)) && (toinst.files[i].flag != F_DIRECTORY)) 283 return 1; 284 } 285 286 dup = strdup(toinst.files[i].name); 287 288 if (dup[strlen(dup)-1] == '/') { 289 if (!stat(toinst.files[i].name, &statret)) { 290 if (!S_ISDIR(statret.st_mode)) { 291 // We expected dir 292 free(dup); 293 return 1; 294 } 295 } 296 } 297 298 while ((ptr = strrchr(dup, '/')) != NULL) { 299 *ptr = '\0'; 300 301 if (!stat(dup, &statret)) { 302 if (!S_ISDIR(statret.st_mode)) { 303 // The some file exists but is not a directory 304 free(dup); 305 return 1; 306 } 307 } 308 } 309 free(dup); 310 } 311 return 0; 312 } 313 314 // DELETEPKGFILES() 315 // 316 // Deletes files of the package. Calls ensurermdir() for each file. 317 // 318 // Parameters: 319 // pkginfo which files will be removed 320 // 321 // Return value: 322 // number of files which failed to be removed 323 int deletepkgfiles(pkginfo torem, unipkgconfig *conf) { 324 unsigned long i; 325 int reval=0; 326 struct stat statbuf; 327 328 // Because we expect at least one in the code below. 329 if (torem.filecount <= 0) 330 return 0; 331 332 /* We call the removal in reverse order than the files are stored. */ 333 /* Why? It's sort of a trick, exploiting that all archives so far used */ 334 /* have the directories needed for files creation in archive before the*/ 335 /* particular file. Therefore, if we reverse the order, we can ensure */ 336 /* proper removal. We may be forced to sort the files later, if this */ 337 /* trick proves uneffective. So far, it works fine (eg. dpkg uses it). */ 338 i = torem.filecount; 339 do { 340 i--; 341 if (torem.files[i].name == NULL) // Because backing up already handles the file 342 continue; 343 344 if (!lstat(torem.files[i].name, &statbuf)) { 345 if (S_ISDIR(statbuf.st_mode)) { 346 rmdir(torem.files[i].name); 347 } else { 348 if (unlink(torem.files[i].name)) { 349 reval += 1; 350 // File is there but we can't remove it. 351 } 352 } 353 } else { 354 // We check if it isn't a possible dir we removed earlier. 355 // Unfortunately, fisdir() is very slow 356 // FIXME 357 if ((conf->ensurermdir == 0) || (!fisdir(torem.files[i].name))) { 358 // File we expected isn't there 359 reval += 1; 360 } 361 } 362 363 // This is a neat call. It is especially useful 364 // if we use obsolete db from pre-0.6 times. 365 if (conf->ensurermdir == 1) 366 ensurermdir(torem.files[i].name); 367 } while (i > 0); 368 return reval; 369 } 370 371 // DIRSANITYCHECK() 372 // 373 // Checks for weird filenames in the checked package. Generally what absolutize 374 // does, but without actually absolutizing the path first. 375 // 376 // Parameters: 377 // pointer to pkginfo structure which is checked 378 // 379 // Return value: 380 // 0 if sane 381 // 2 if possible hacking attempt 382 // (1 is reserved) 383 int dirsanitycheck(pkginfo *checked) { 384 char *ptr; 385 unsigned long i; 386 387 for (i=0; i<checked->filecount; i++) { 388 if ((ptr = strstr(checked->files[i].name, "~/")) != NULL) { 389 if (ptr == checked->files[i].name) { return 2; } else { if (*(ptr-1) == '/') { return 2; } } // At start of filename or after dirname 390 } 391 if ((ptr = strstr(checked->files[i].name, "../")) != NULL) { 392 if (ptr == checked->files[i].name) { return 2; } else { if (*(ptr-1) == '/') { return 2; } } // At start of filename or after dirname 393 } 394 } 395 return 0; 396 } 397 398 // MAKEBACKUPS() 399 // 400 // Makes backups of all files which are in directories that are keeped 401 // protected (this info is in unipkg config). 402 // 403 // Parameters: 404 // pointer to unipkg config structure 405 // pointer to pkginfo describing the package 406 // 407 // Return value: 408 // 0 if no problem 409 // 1 if troubles when backing up 410 int makebackups(unipkgconfig *conf, pkginfo *pinfo) { 411 unsigned long i, j, k, matchlen, writelen; 412 char *absbackup, *timebuf, *namebuf; 413 struct stat statbuf; 414 struct tm timemark; 415 time_t timestamp; 416 int reval; 417 418 reval = 0; 419 for (j=0; conf->protecteddirs[j] != NULL; j++) { 420 absbackup = absolutizestr(conf->protecteddirs[j], conf->instdir); 421 matchlen = strlen(absbackup); 422 for (i=0; i<pinfo->filecount; i++) { 423 if (!strncmp(pinfo->files[i].name, absbackup, matchlen)) { 424 if (!lstat(pinfo->files[i].name, &statbuf)) { 425 if (S_ISDIR(statbuf.st_mode)) continue; 426 timebuf = malloc(50); 427 timestamp = time(NULL); 428 gmtime_r(×tamp, &timemark); 429 namebuf = malloc(strlen(pinfo->files[i].name)+strftime(timebuf, 100, "%d-%m-%Y", &timemark)+20); // /foo/bar/something.unipkg-backup-DATE-number 430 strcpy(namebuf, pinfo->files[i].name); 431 strcat(namebuf, ".unipkg-backup_"); 432 strcat(namebuf, timebuf); 433 strcat(namebuf, "_"); 434 writelen = strlen(namebuf); 435 436 timebuf = realloc(timebuf, 4); 437 k=0; 438 snprintf(timebuf, 3, "%ld", k); 439 strcat(namebuf, timebuf); 440 while (!lstat(namebuf, &statbuf)) { 441 if (k>MAXATTEMPTS) { 442 reval = 2; 443 break; 444 } 445 namebuf[writelen] = '\0'; 446 snprintf(timebuf, 3, "%ld", k); 447 strcat(namebuf, timebuf); 448 k++; 449 } 450 free(timebuf); 451 if (reval == 2) { 452 reval = 1; 453 } else { 454 if (rename(pinfo->files[i].name, namebuf)) { reval = 1; } 455 } 456 free(namebuf); 457 } 458 } 459 } 460 free(absbackup); 461 } 462 return reval; 463 } 464 465 // OVERWRITECHECK() 466 // 467 // Checks if the new package will be installable. This should be rather strict, 468 // since if we mess up with something like glibc, we've messed up terminally. 469 // It finds files that are in newpkg but not in oldpkg and then feeds them to 470 // preinstcheck to find out if we can write those, too. 471 // 472 // Parameters: 473 // the obsoleted package 474 // the new package 475 // 476 // Return value: 477 // 0 if okay 478 // 1 if possible failure during installation 479 int overwritecheck(pkginfo *newpkg, pkginfo *oldpkg, unipkgconfig *conf) { 480 unsigned long i, j; 481 pkginfo output; 482 int amatch; 483 char *absbackup; 484 485 clearpinfo(&output); 486 487 // 488 // The purpose of this is putting files that are in newpkg but not in 489 // oldpkg into the needle pkginfo, so we may use it later for matching 490 // files. 491 // 492 for (i=0; i<newpkg->filecount; i++) { 493 amatch = 0; 494 for (j=0; j<oldpkg->filecount; j++) { 495 if (!strcmp(newpkg->files[i].name, oldpkg->files[j].name)) { 496 amatch = 1; 497 break; 498 } 499 } 500 501 if (amatch == 0) { 502 for (j=0; conf->protecteddirs[j] != NULL; j++) { 503 absbackup=absolutizestr(conf->protecteddirs[j], conf->instdir); 504 if (!strncmp(newpkg->files[i].name, absbackup, strlen(absbackup))) { 505 amatch = 1; 506 free(absbackup); 507 break; 508 } 509 free(absbackup); 510 } 511 512 if (amatch == 0) { 513 addfile(&output, strdup(newpkg->files[i].name), newpkg->files[i].flag); 514 } 515 } 516 } 517 518 if (output.filecount == 0) { 519 freepinfo(&output); 520 return 0; 521 } 522 523 // We check if any of them actually do colide. 524 amatch = preinstcheck(output); 525 freepinfo(&output); 526 return amatch; 527 } 528 529 // GETACTION() 530 // 531 // Loops through array of strings and if one matches needle, returns the index 532 // 533 // Parameters: 534 // the needle 535 // the array of strings 536 // 537 // Return value: 538 // 0 if no match 539 // id of the matched action otherwise 540 int getaction(char *needle, actions *longopts) { 541 unsigned int i=0; 542 543 while (longopts[i].text != NULL) { 544 if (strcmp(needle, longopts[i].text) == 0) { return longopts[i].reval; } 545 i++; 546 } 547 return 0; 548 } 549 550 551 // RUNSCRIPT() 552 // 553 // Runs a script. If second parameter is not 1, it returns 0 even before 554 // actually running the script 555 // 556 // Parameters: 557 // the script 558 // integer defining action parameters 559 // integer defining whether to run the script 560 // 561 // Return value: 562 // 0 if no problem 563 // 1 if I/O error occurs (cannot write to pipe) 564 // 2 if pipe couldn't be opened -- reserved 565 // 3 if the script itself failed 566 int runscript(lstring script, int action, int usescript) { 567 char *fn, *call; 568 int fd; 569 int reval; 570 unsigned long written, calllen; 571 572 if (usescript != 1) return 0; 573 if (script.len == 0) return 0; 574 if (script.data == NULL) return 0; 575 576 #ifndef P_tmpdir 577 #define P_tmpdir "/tmp" 578 #endif 579 fn = malloc(strlen(P_tmpdir) + 15); // "unipkg." + 6*X + / + \0 580 strcpy(fn, P_tmpdir); 581 strcat(fn, "/unipkg.XXXXXX"); 582 583 fd = mkstemp(fn); 584 if (fd == -1) { 585 free(fn); 586 return 1; 587 } 588 589 for (written = 0; written < script.len; written+=1024) { 590 write(fd, script.data + written, ((script.len - written) < 1024) ? (script.len - written) : 1024); 591 } 592 fchmod(fd, 0700); 593 close(fd); 594 595 calllen = strlen(fn); 596 if (script.parameters[action] != NULL) { 597 calllen += strlen(script.parameters[action]) + 2; 598 } else if (script.parameters[A_IRRELEVANT] != NULL) { 599 calllen += strlen(script.parameters[A_IRRELEVANT]) + 2; 600 } 601 calllen += strlen(REDIRECT); 602 calllen += 1; 603 604 call = malloc(calllen); 605 606 strcpy(call, fn); 607 if (script.parameters[action] != NULL) { 608 strcat(call, " "); 609 strcat(call, script.parameters[action]); 610 strcat(call, " "); 611 } else if (script.parameters[A_IRRELEVANT] != NULL) { 612 strcat(call, " "); 613 strcat(call, script.parameters[A_IRRELEVANT]); 614 strcat(call, " "); 615 } 616 strcat(call, REDIRECT); 617 618 fprintf(stderr, "Call: '%s'", call); 619 620 reval = system(call); 621 free(call); 622 623 if (reval == -1) { 624 reval = 1; 625 } else if (WEXITSTATUS(reval)) { 626 reval = 3; 627 } else { 628 reval = 0; 629 } 630 631 unlink(fn); 632 free(fn); 633 634 return reval; 635 } 636 637 638 /////////////////////////////////////////////////////////////////////////////// 639 // // 640 // D Y N A M I C F U N C T I O N S L O A D I N G // 641 // // 642 /////////////////////////////////////////////////////////////////////////////// 643 644 // SYNCDBSTUB() 645 // 646 // Stub for the syncdb call, which isn't mandatory in a module. 647 int syncdbStub(void *handle) { 648 return 0; 649 } 650 651 // LOADDBCALLS() 652 // 653 // Loads function calls which are used to handle database. 654 // 655 // Parameters: 656 // pointer to the databasecalls structure 657 // 658 // Return value: 659 // 0 if okay 660 // 1 if error 661 int loaddbcalls(databasecalls *dbcalls) { 662 dbcalls->opendb = (void* (*)(char*))dlsym(dbcalls->libhandle, "opendb"); 663 dbcalls->closedb = (void (*)(void*))dlsym(dbcalls->libhandle, "closedb"); 664 dbcalls->rewinddb = (void (*)(void*))dlsym(dbcalls->libhandle, "rewinddb"); 665 dbcalls->writepkg = (int (*)(void*, pkginfo*))dlsym(dbcalls->libhandle, "writepkg"); 666 dbcalls->readpkg = (int (*)(void*, pkginfo*))dlsym(dbcalls->libhandle, "readpkg"); 667 dbcalls->delpkg = (int (*)(void*, char*))dlsym(dbcalls->libhandle, "delpkg"); 668 dbcalls->findpkg = (int (*)(void*, pkginfo, pkginfo*, int))dlsym(dbcalls->libhandle, "findpkg"); 669 dbcalls->iswriteable = (int (*)(void*))dlsym(dbcalls->libhandle, "iswriteable"); 670 if (dlerror() != NULL) { return 1; } 671 672 dbcalls->syncdb = (int (*)(void *))dlsym(dbcalls->libhandle, "syncdb"); 673 if(!dbcalls->syncdb) dbcalls->syncdb = syncdbStub; 674 675 return 0; 676 } 677 678 // LOADPKGCALLS() 679 // 680 // Loads function calls which are used to handle packages. 681 // 682 // Parameters: 683 // pointer to the packagecalls structure 684 // 685 // Return value: 686 // 0 if okay 687 // 1 if error 688 int loadpkgcalls(packagecalls *pkgcalls) { 689 pkgcalls->identify = (int (*)(packdef))dlsym(pkgcalls->libhandle, "identify"); 690 pkgcalls->pkgdetails = (int (*)(packdef, pkginfo*))dlsym(pkgcalls->libhandle, "pkgdetails"); 691 pkgcalls->pkginstall = (int (*)(packdef))dlsym(pkgcalls->libhandle, "pkginstall"); 692 if (dlerror() != NULL) { return 1; } 693 return 0; 694 } 695 696 697 /////////////////////////////////////////////////////////////////////////////// 698 // // 699 // C O N F I G H A N D L I N G F U N C T I O N S // 700 // // 701 /////////////////////////////////////////////////////////////////////////////// 702 703 // PARSELIST() 704 // 705 // Parses a list of members, colon separated, into an array. 706 // 707 // Parameters: 708 // the source string 709 // array of members, this is in fact output 710 // 711 // Return value: 712 // 0, always 713 int parselist(char *ptr, char ***list) { 714 char *pptr, *sptr; 715 unsigned long libcount; 716 717 sptr = ptr; 718 719 libcount = 0; 720 *list = realloc(*list, sizeof(char *)); 721 (*list)[0] = NULL; 722 723 while ((pptr = strchr(sptr, ',')) != NULL) { 724 *pptr = 0x0; 725 726 libcount++; 727 *list = realloc(*list, (libcount+1) * sizeof(char *)); 728 (*list)[libcount-1] = strdup(sptr); 729 (*list)[libcount] = NULL; 730 731 sptr = pptr + 1; 732 while (*sptr == ' ') { 733 sptr++; 734 } 735 } 736 737 libcount++; 738 *list = realloc(*list, (libcount+1) * sizeof(char *)); 739 (*list)[libcount-1] = strdup(sptr); 740 (*list)[libcount] = NULL; 741 742 return 0; 743 } 744 745 // PARSECONFIG() 746 // 747 // Parses the unipkg config in a very trivial way. 748 // 749 // Parameters: 750 // the path to config 751 // pointer to unipkg config structure 752 // 753 // Return value: 754 // 0 if okay 755 // 1 if error 756 int parseconfig(char *path, unipkgconfig *conf) { 757 FILE *fp; 758 char *buf, *dptr, *ptr; 759 unsigned long bufsize, slen, i; 760 actions configopts[] = {{"handlescripts", 1}, 761 {"dbpath", 2}, 762 {"datadir", 3}, 763 {"packagelibs", 4}, 764 {"dblib", 5}, 765 {"protecteddirs", 6}, 766 {"ignorescriptfail", 7}, 767 {"ensurermdir", 8}, 768 {NULL, 0} }; 769 770 bufsize = 128; 771 buf = malloc(bufsize); 772 773 fp = fopen(path, "r"); 774 if (fp == NULL) { return 1; } 775 776 do { 777 do { 778 if (fgets(buf, bufsize, fp) == NULL) { 779 if (!feof(fp)) { // We got trouble with fgets, but we didn't feof ... What?! 780 free(buf); 781 return 1; 782 } 783 break; 784 } 785 slen = strnlen(buf, bufsize) - 1; 786 if ((buf[slen] != '\n') && (buf[slen] != 0x0)) { 787 bufsize += bufsize; buf = realloc(buf, bufsize); 788 } 789 } while ((buf[slen] != '\n') && (buf[slen] != 0x0)); // After this, buf contains whole line properly 790 slen = strnlen(buf, bufsize) - 1; 791 if (buf[slen] == '\n') { buf[slen] = 0x0; } 792 793 if ((buf[0] != '#')&&(buf[0] != ' ')&&(buf[0] != 0x0)) { // # is comment ... We also ignore any line that doesn't start with the value 794 if ((dptr = strchr(buf, '=')) != NULL) { // Could we find '=' ? 795 *dptr = 0x0; 796 dptr++; 797 798 while (*dptr == ' '){ 799 dptr++; 800 } 801 ptr = dptr; 802 803 if ((dptr = strchr(buf, ' ')) != NULL) { *dptr = 0x0; } // Remove the spaces after the keyword 804 805 // This is very messy, so to make it clear: 806 // buf contains the part of the line before '=', terminated by '=' or ' ' 807 // ptr contains the other part, terminated by newline 808 809 switch(getaction(buf, configopts)) { 810 case 1: 811 conf->usescripts = atol(ptr); 812 break; 813 case 2: 814 if (conf->dbpath != NULL) { free(conf->dbpath); } 815 conf->dbpath = strdup(ptr); 816 break; 817 case 3: 818 if (conf->instdir != NULL) { free(conf->instdir); } 819 conf->instdir = strdup(ptr); 820 break; 821 case 4: 822 for (i=0; conf->pkg_libraries[i] != NULL; i++) { 823 free(conf->pkg_libraries[i]); 824 } 825 conf->pkg_libraries = realloc(conf->pkg_libraries, sizeof(char*)); 826 827 parselist(ptr, &(conf->pkg_libraries)); 828 break; 829 case 5: 830 if (conf->dblib != NULL) { free(conf->dblib); } 831 832 conf->dblib = strdup(ptr); 833 break; 834 case 6: 835 for (i=0; conf->protecteddirs[i] != NULL; i++) { 836 free(conf->protecteddirs[i]); 837 } 838 conf->protecteddirs = realloc(conf->protecteddirs, sizeof(char*)); 839 840 parselist(ptr, &(conf->protecteddirs)); 841 break; 842 case 7: 843 conf->failsafe = atol(ptr); 844 break; 845 case 8: 846 conf->ensurermdir = atol(ptr); 847 break; 848 } 849 } 850 } 851 } while (!feof(fp)); 852 fclose(fp); 853 free(buf); 854 return 0; 855 } 856 857 // CLEARCONFIG() 858 // 859 // Clears all members of unipkg config structure, similar to clearpinfo 860 // 861 // Parameters: 862 // pointer to the unipkg config structure 863 // 864 // Return value: 865 // none 866 void clearconfig(unipkgconfig *conf) { 867 conf->usescripts = -1; 868 conf->failsafe = -1; 869 conf->ensurermdir = 0; 870 conf->dbpath = NULL; 871 conf->dblib = NULL; 872 conf->instdir = NULL; 873 conf->dbpath = NULL; 874 conf->pkg_libraries = malloc(sizeof(char *)); 875 conf->pkg_libraries[0] = NULL; 876 conf->protecteddirs = malloc(sizeof(char *)); 877 conf->protecteddirs[0] = NULL; 878 } 879 880 // SANECONFIG() 881 // 882 // Sets members of unipkg config structure to default values 883 // 884 // Parameters: 885 // pointer to the unipkg config structure 886 // 887 // Return value: 888 // none 889 void saneconfig(unipkgconfig *conf) { 890 if (conf->dbpath == NULL) { 891 conf->dbpath = strdup(DBPATH); 892 } 893 if (conf->instdir == NULL) { 894 conf->instdir = strdup(DATAPREFIX); 895 } 896 if (conf->dblib == NULL) { 897 conf->dblib = strdup(DEFDB); 898 } 899 if (conf->pkg_libraries[0] == NULL) { 900 conf->pkg_libraries = realloc(conf->pkg_libraries, 5 * sizeof(char *)); 901 conf->pkg_libraries[0] = strdup("libunipkg-rpm.so.1"); 902 conf->pkg_libraries[1] = strdup("libunipkg-deb.so.1"); 903 conf->pkg_libraries[2] = strdup("libunipkg-slack.so.1"); 904 conf->pkg_libraries[3] = strdup("libunipkg-arch.so.1"); 905 conf->pkg_libraries[4] = NULL; 906 } 907 } 908 909 // FREECONFIG() 910 // 911 // Frees all members of unipkg config structure, similar to freepinfo 912 // 913 // Parameters: 914 // pointer to the unipkg config structure 915 // 916 // Return value: 917 // none 918 void freeconfig(unipkgconfig *conf) { 919 unsigned long i; 920 free(conf->dbpath); 921 free(conf->instdir); 922 for (i=0; conf->pkg_libraries[i] != NULL; i++) { 923 free(conf->pkg_libraries[i]); 924 } 925 free(conf->pkg_libraries); 926 927 for (i=0; conf->protecteddirs[i] != NULL; i++) { 928 free(conf->protecteddirs[i]); 929 } 930 free(conf->protecteddirs); 931 932 free(conf->dblib); 933 } 934 935 /////////////////////////////////////////////////////////////////////////////// 936 // // 937 // C O R E O F U P K G // 938 // // 939 /////////////////////////////////////////////////////////////////////////////// 940 int main(int argc, char **argv) { 941 // Basic variables 942 packdef pdef; 943 void *dbhandle; 944 unsigned long libid, i; 945 pkginfo newpkg, pinfo, needle, blocker; 946 int rv, scriptrv; 947 int argind; 948 949 // More function pointers for DB IO 950 databasecalls dbcalls; 951 packagecalls pkgcalls; 952 953 // Configuration 954 unipkgconfig conf; 955 // Common setup 956 actions longopts[] = { {"install", 1}, 957 {"remove", 2}, 958 {"list", 3}, 959 {"find", 4}, 960 {"info", 5}, 961 {"findfile", 6}, 962 {"listfiles", 7}, 963 {"preinfo", 8}, 964 {"execute", 9}, 965 {NULL, 0} }; 966 967 if (argc < 2) { Usage(); return 1; } 968 969 clearconfig(&conf); 970 parseconfig(CONFPATH, &conf); 971 saneconfig(&conf); 972 973 dbcalls.libhandle = dlopen(conf.dblib, RTLD_NOW); 974 if (dbcalls.libhandle == NULL) { 975 fprintf(stderr, "Error: %s\n", dlerror()); 976 freeconfig(&conf); 977 exit(1); 978 } 979 980 if (loaddbcalls(&dbcalls)) { 981 fprintf(stderr, "Couldn't load all DB functions."); 982 freeconfig(&conf); 983 exit(1); 984 } 985 986 /*************************************\ 987 * Parsing the actions - this is where * 988 * all the fun is * 989 \*************************************/ 990 switch (getaction(argv[1], longopts)) { 991 case 1: // Install 992 if (argc < 3) { 993 Usage(); 994 dlclose(dbcalls.libhandle); 995 freeconfig(&conf); 996 return 1; 997 } 998 999 // Loop through all packages. 1000 for (argind = 2; argind < argc; argind++) { 1001 progressinfo("Preparing... "); 1002 pdef.filepointer=fopen(argv[argind], "r"); 1003 if (pdef.filepointer == NULL) { 1004 progressfinish("failed: couldn't open file.\n"); 1005 freeconfig(&conf); 1006 dlclose(dbcalls.libhandle); 1007 exit(1); 1008 } 1009 progressfinish("succeeded [%d/%d].\n", argind-1, argc-2); 1010 pdef.filename = strdup(argv[argind]); 1011 pdef.destdir = strdup(conf.instdir); 1012 pdef.action = A_INSTALL; 1013 pdef.pinfo = &newpkg; 1014 1015 dbhandle = NULL; 1016 progressinfo("Loading database... "); 1017 if ((dbhandle = dbcalls.opendb(conf.dbpath)) != NULL) { // Can we open db? 1018 if (dbcalls.iswriteable(dbhandle)) { 1019 progressfinish("succeeded.\n"); 1020 } else { 1021 progressfinish("failed: you are not permitted to write to the db.\n"); 1022 fclose(pdef.filepointer); 1023 free(pdef.filename); 1024 free(pdef.destdir); 1025 freeconfig(&conf); 1026 dbcalls.closedb(dbhandle); 1027 dlclose(dbcalls.libhandle); 1028 exit(1); 1029 } 1030 1031 // So we can write to the db, what's next? 1032 // Loading proper lib and functions 1033 progressinfo("Identifying package... "); 1034 for (libid=0; conf.pkg_libraries[libid] != NULL; libid++) { 1035 pkgcalls.libhandle = dlopen(conf.pkg_libraries[libid], RTLD_NOW); 1036 if (pkgcalls.libhandle == NULL) { 1037 progressfinish("failed: couldn't open library %s.\ndlopen() error: %s\n", conf.pkg_libraries[libid], dlerror()); 1038 continue; 1039 } 1040 if (loadpkgcalls(&pkgcalls)) { 1041 progressfinish("failed: couldn't load all library functions, library %s.\ndlsym() error: %s\n", conf.pkg_libraries[libid], dlerror()); 1042 continue; 1043 } 1044 if (pkgcalls.identify(pdef) == 0) { break; } 1045 dlclose(pkgcalls.libhandle); 1046 } 1047 1048 if (conf.pkg_libraries[libid] == NULL) { 1049 progressfinish("failed: unknown package.\n"); 1050 fclose(pdef.filepointer); 1051 free(pdef.filename); 1052 free(pdef.destdir); 1053 freeconfig(&conf); 1054 dbcalls.closedb(dbhandle); 1055 dlclose(dbcalls.libhandle); 1056 exit(1); 1057 } 1058 1059 progressfinish("succeeded.\n"); 1060 1061 clearpinfo(&newpkg); 1062 clearpinfo(&needle); 1063 clearpinfo(&pinfo); 1064 1065 progressinfo("Reading package... "); 1066 if (pkgcalls.pkgdetails(pdef, &newpkg)) { 1067 progressfinish("failed: package may be broken.\n"); 1068 fclose(pdef.filepointer); 1069 free(pdef.filename); 1070 freepinfo(&newpkg); 1071 dlclose(pkgcalls.libhandle); 1072 dbcalls.closedb(dbhandle); 1073 dlclose(dbcalls.libhandle); 1074 exit(1); 1075 } 1076 else { 1077 progressfinish("succeeded.\n"); 1078 } 1079 1080 switch (absolutize(&newpkg, conf.instdir)) { 1081 case 1: 1082 fprintf(stderr, "\nSomewhy couldn't absolutize the path name. Please, report this message, along with the package causing it - it should not happen."); 1083 dlclose(pkgcalls.libhandle); 1084 fclose(pdef.filepointer); 1085 free(pdef.filename); 1086 freepinfo(&newpkg); 1087 freeconfig(&conf); 1088 dbcalls.closedb(dbhandle); 1089 dlclose(dbcalls.libhandle); 1090 exit(1); 1091 break; 1092 case 2: 1093 // This may be handled a bit differently 1094 fprintf(stderr, "\n The package seems to contain paths that shouldn't be there,\n" 1095 "namely '../' and '~/' may be attempt to do something nasty within your\n" 1096 "homedir (especially if you are root), or get outside the datadir you specified\n" 1097 "in config. It's generally good idea NOT to install these packages.\n" 1098 "If you are anyhow confused by this warning,\n DO NOT install the package.\n"); 1099 if (yesnoquestion(" Do you really, honestly (no barley, cross fingers) want to install it?\n [y/N] ", 'Y')) { 1100 progressinfo("Carrying on..."); 1101 progressfinish("You've been warned!"); 1102 } 1103 else { 1104 fprintf(stderr, "Terminating installation.\n"); 1105 dlclose(pkgcalls.libhandle); 1106 fclose(pdef.filepointer); 1107 free(pdef.filename); 1108 freepinfo(&newpkg); 1109 freeconfig(&conf); 1110 dbcalls.closedb(dbhandle); 1111 dlclose(dbcalls.libhandle); 1112 exit(1); 1113 } 1114 break; 1115 } 1116 1117 dbcalls.rewinddb(dbhandle); 1118 1119 if (conf.usescripts == -1) { 1120 conf.usescripts = yesnoquestion("\n Do you want to have the preinst, postinst, prerm and postrm scripts run?\n" 1121 " They may fail, although it's not critical.\n Remember, they have been written for a specific distribution.\n" 1122 " NO WARRANTY, it's suggested NOT to run them.\n Check README before you say 'y'.\n [y/N] ", 'Y'); 1123 } 1124 1125 signal(2, siginthandle); 1126 progressinfo("Checking database... "); 1127 1128 // a lot of files, eg. OpenOffice 1129 clearpinfo(&needle); 1130 dupexisting(&needle, newpkg); 1131 1132 // Check if some pkg in fact bears our name. 1133 if ((rv = dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_NAME | MATCH_OVRWR)) != -1) { 1134 // No? Then check if some package bears our files. If yes, trouble. 1135 rv = dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_FILE | MATCH_OVRWR); 1136 } 1137 // Now rv contains: 1138 // 1 if some package has our files but not our name 1139 // 0 if no package bears our name or files 1140 // -1 if some package bears our name 1141 1142 switch (rv) { 1143 case 1: // Found, name does NOT match the name of needle 1144 progressfinish("failed: package collision by %s\n", pinfo.name); 1145 freepinfo(&pinfo); 1146 freepinfo(&needle); 1147 break; 1148 case -1: // Found, name matches name of needle 1149 1150 // Did we find any more matches? 1151 // That is, is there some other package that has our files which the package that 1152 // bears our name doesn't have? 1153 freepinfo(&needle); 1154 clearpinfo(&needle); 1155 substractfiles(newpkg, pinfo, &needle); 1156 1157 clearpinfo(&blocker); 1158 if (dbcalls.findpkg(dbhandle, needle, &blocker, MATCH_FILE) == 1) { 1159 progressfinish("failed: package collision by %s\n", blocker.name); 1160 freepinfo(&needle); 1161 freepinfo(&blocker); 1162 break; 1163 } 1164 1165 // Or perhaps some files present in filesystem that the old pkg doesn't have 1166 // and thus new pkg cannot safely overwrite? 1167 if (overwritecheck(&newpkg, &pinfo, &conf)) { 1168 progressfinish("failed: there are files colliding with this package.\n"); 1169 whichcollideexceptknown(newpkg, pinfo); 1170 freepinfo(&needle); 1171 break; 1172 } 1173 1174 switch (versioncmp(pinfo.version, newpkg.version)) { 1175 case -1: 1176 pdef.action = A_UPGRADE; 1177 break; 1178 case 0: 1179 pdef.action = A_REINSTALL; 1180 break; 1181 case 1: 1182 pdef.action = A_DOWNGRADE; 1183 break; 1184 } 1185 1186 // No? Then remove the obsolete at once! 1187 progressfinish("succeeded.\n"); 1188 1189 if (conf.usescripts == 1) { 1190 progressinfo("Running preremove... "); 1191 1192 scriptrv = runscript(pinfo.prerm, pdef.action, conf.usescripts); 1193 if (scriptrv != 0) { 1194 switch (scriptrv) { 1195 case 1: 1196 progressfinish("failed: I/O error when attempting to run preremove\n"); 1197 break; 1198 case 2: 1199 progressfinish("failed: fork() failed when attempting to run preremove\n"); 1200 break; 1201 case 3: 1202 progressfinish("failed: preremove script returned an error\n"); 1203 break; 1204 } 1205 1206 if (conf.failsafe == -1) { 1207 conf.failsafe = yesnoquestion("\n Handling script failed. Do you want to continue anyway?\n [y/N] ", 'Y'); 1208 } 1209 1210 if (!conf.failsafe) { 1211 freepinfo(&needle); 1212 // Leaving the install switch and moving to cleanup. 1213 break; 1214 } 1215 } else { 1216 progressfinish("succeeded.\n"); 1217 } 1218 } 1219 1220 progressinfo("Ensuring backups... "); 1221 if (!makebackups(&conf, &pinfo)) { 1222 progressfinish("succeeded.\n"); 1223 } else { 1224 progressfinish("failed: some files couldn't be backed up. Perhaps a race condition attempt.\n"); 1225 } 1226 1227 progressinfo("Deleting old files... "); 1228 if (deletepkgfiles(pinfo, &conf)) { 1229 progressfinish("warning - some files failed to be removed.\n"); 1230 } else { 1231 progressfinish("succeeded.\n"); 1232 } 1233 1234 progressinfo("Deleting from db... "); 1235 if (dbcalls.delpkg(dbhandle, pinfo.name)) { 1236 progressfinish("failed - perhaps you do not have permissions.\n"); 1237 } else { 1238 progressfinish("succeeded.\n"); 1239 } 1240 1241 if (conf.usescripts == 1) { 1242 progressinfo("Running postremove... "); 1243 1244 scriptrv = runscript(pinfo.postrm, pdef.action, conf.usescripts); 1245 if (scriptrv != 0) { 1246 switch (scriptrv) { 1247 case 1: 1248 progressfinish("failed: I/O error when attempting to run postremove\n"); 1249 break; 1250 case 2: 1251 progressfinish("failed: fork() failed when attempting to run postremove\n"); 1252 break; 1253 case 3: 1254 progressfinish("failed: postremove script returned an error\n"); 1255 break; 1256 } 1257 1258 progressinfo("Warning... "); 1259 progressfinish("postremove failure ignored.\n"); 1260 } else { 1261 progressfinish("succeeded.\n"); 1262 } 1263 } 1264 1265 progressinfo("Cleaning up... "); 1266 freepinfo(&pinfo); 1267 case 0: 1268 freepinfo(&needle); 1269 progressfinish("succeeded.\n"); 1270 // Do the below. 1271 1272 progressinfo("Ensuring backups... "); 1273 if (!makebackups(&conf, &newpkg)) { 1274 progressfinish("succeeded.\n"); 1275 } else { 1276 progressfinish("failed: some files couldn't be backed up.\n"); 1277 } 1278 1279 progressinfo("Checking environment... "); 1280 1281 if (!preinstcheck(newpkg)) { // Aren't the files in the system installed some other way? 1282 if (!dbcalls.writepkg(dbhandle, &newpkg)) { // Can we write to the database? 1283 progressfinish("succeeded.\n"); 1284 1285 if (conf.usescripts == 1) { 1286 progressinfo("Running preinstall... "); 1287 1288 scriptrv = runscript(newpkg.preinst, pdef.action, conf.usescripts); 1289 if (scriptrv != 0) { 1290 switch (scriptrv) { 1291 case 1: 1292 progressfinish("failed: I/O error when attempting to run preinstall\n"); 1293 break; 1294 case 2: 1295 progressfinish("failed: fork() failed when attempting to run preinstall\n"); 1296 break; 1297 case 3: 1298 progressfinish("failed: preinstall script returned an error\n"); 1299 break; 1300 } 1301 1302 if (conf.failsafe == -1) { 1303 conf.failsafe = yesnoquestion("\n Handling script failed. Do you want to continue anyway?\n [y/N] ", 'Y'); 1304 } 1305 1306 if (!conf.failsafe) { 1307 freepinfo(&needle); 1308 // Leaving the install switch and moving to cleanup. 1309 break; 1310 } 1311 } else { 1312 progressfinish("succeeded.\n"); 1313 } 1314 } 1315 1316 progressinfo("Installing... "); 1317 1318 scriptrv = pkgcalls.pkginstall(pdef); 1319 if (scriptrv) { 1320 switch (scriptrv) { 1321 case 1: 1322 progressfinish("failed: package corrupted.\n"); 1323 break; 1324 1325 case 2: 1326 progressfinish("failed: unable to unpack data.\n"); 1327 break; 1328 1329 case 3: 1330 progressfinish("failed: unexpected file overwrite attempt.\n"); 1331 break; 1332 } 1333 1334 if (!yesnoquestion("Do you want the installed files to be removed (it will probably return failure because not all will be removed, but what can be removed will be removed?\n [Y/n] ", 'N')) { 1335 progressinfo("Cleaning up files... "); 1336 if (deletepkgfiles(newpkg, &conf)) { 1337 progressfinish("warning - some files failed to be removed.\n"); 1338 } else { 1339 progressfinish("succeeded.\n"); 1340 } 1341 1342 progressinfo("Cleaning up database... "); 1343 if (dbcalls.delpkg(dbhandle, newpkg.name)) { 1344 progressfinish("failed: couldn't delete writeup from database.\n"); 1345 } else { 1346 progressfinish("succeeded.\n"); 1347 } 1348 } 1349 1350 // Leaving the install switch and moving to cleanup. 1351 break; 1352 } else { 1353 progressfinish("succeeded.\n"); 1354 } 1355 1356 if (conf.usescripts == 1) { 1357 progressinfo("Running postinstall... "); 1358 1359 scriptrv = runscript(newpkg.postinst, pdef.action, conf.usescripts); 1360 if (scriptrv != 0) { 1361 switch (scriptrv) { 1362 case 1: 1363 progressfinish("failed: I/O error when attempting to run postinstall\n"); 1364 break; 1365 case 2: 1366 progressfinish("failed: fork() failed when attempting to run postinstall\n"); 1367 break; 1368 case 3: 1369 progressfinish("failed: postinstall script returned an error\n"); 1370 break; 1371 } 1372 1373 progressinfo("Warning... "); 1374 progressfinish("postremove failure ignored.\n"); 1375 } else { 1376 progressfinish("succeeded.\n"); 1377 } 1378 } 1379 } // end of if about db writing 1380 else { 1381 progressfinish("failed: couldn't write to db (perhaps you are not privileged user?).\n"); 1382 } 1383 } // end of if about pkg checking 1384 else { 1385 progressfinish("failed: there are files colliding with this package.\n"); 1386 whichcollide(newpkg); 1387 } 1388 break; 1389 } // end of big switch 1390 signal(2, SIG_DFL); 1391 dbcalls.closedb(dbhandle); 1392 } // end of if about db opening 1393 else { 1394 progressfinish("failed: couldn't open database.\n"); 1395 } 1396 freepinfo(&newpkg); 1397 1398 fclose(pdef.filepointer); 1399 free(pdef.filename); 1400 free(pdef.destdir); 1401 dlclose(pkgcalls.libhandle); 1402 } // End of the argind for loop. 1403 break; 1404 1405 case 2: // Remove 1406 if (argc < 3) { 1407 Usage(); 1408 dlclose(dbcalls.libhandle); 1409 freeconfig(&conf); 1410 return 1; 1411 } 1412 1413 dbhandle = NULL; 1414 progressinfo("Loading database... "); 1415 if ((dbhandle = dbcalls.opendb(conf.dbpath)) == NULL) { 1416 progressfinish("failed: couldn't open database.\n"); 1417 } 1418 else { 1419 if (dbcalls.iswriteable(dbhandle)) { 1420 progressfinish("succeeded.\n"); 1421 } else { 1422 progressfinish("failed: you are not permitted to write to the db.\n"); 1423 freeconfig(&conf); 1424 dbcalls.closedb(dbhandle); 1425 dlclose(dbcalls.libhandle); 1426 exit(1); 1427 } 1428 1429 for (argind = 2; argind < argc; argind++) { 1430 progressinfo("Selecting package... "); 1431 dbcalls.rewinddb(dbhandle); 1432 needle.name = argv[argind]; 1433 needle.filecount = 0; 1434 pdef.action = A_REMOVE; 1435 clearpinfo(&pinfo); 1436 if (dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_NAME)) { 1437 progressfinish("succeeded [%d/%d].\n", argind-1, argc-2); 1438 1439 if (conf.usescripts == -1) { 1440 conf.usescripts = yesnoquestion("\n Do you want to have the preinst, postinst, prerm and postrm scripts run?\n" 1441 " They may fail, although it's not critical.\n Remember, they have been written for a specific distribution.\n" 1442 " NO WARRANTY, it's suggested NOT to run them.\n Check README before you say 'y'.\n [y/N] ", 'Y'); 1443 } 1444 signal(2, siginthandle); 1445 1446 dbcalls.rewinddb(dbhandle); 1447 1448 if (conf.usescripts == 1) { 1449 progressinfo("Running preremove... "); 1450 1451 scriptrv = runscript(pinfo.prerm, pdef.action, conf.usescripts); 1452 if (scriptrv != 0) { 1453 switch (scriptrv) { 1454 case 1: 1455 progressfinish("failed: I/O error when attempting to run preremove\n"); 1456 break; 1457 case 2: 1458 progressfinish("failed: fork() failed when attempting to run preremove\n"); 1459 break; 1460 case 3: 1461 progressfinish("failed: preremove script returned an error\n"); 1462 break; 1463 } 1464 1465 if (conf.failsafe == -1) { 1466 conf.failsafe = yesnoquestion("\n Handling script failed. Do you want to continue anyway?\n [y/N] ", 'Y'); 1467 } 1468 1469 if (!conf.failsafe) { 1470 // We perform cleanup, return signal handler 1471 // close db and break the whole giant action switch. 1472 freepinfo(&pinfo); 1473 signal(2, SIG_DFL); 1474 dbcalls.closedb(dbhandle); 1475 break; 1476 } 1477 } else { 1478 progressfinish("succeeded.\n"); 1479 } 1480 } 1481 1482 progressinfo("Ensuring backups... "); 1483 if (!makebackups(&conf, &pinfo)) { 1484 progressfinish("succeeded.\n"); 1485 } else { 1486 progressfinish("failed: some files couldn't be backed up. Perhaps a race condition attempt.\n"); 1487 } 1488 1489 progressinfo("Deleting files... "); 1490 if (deletepkgfiles(pinfo, &conf)) { 1491 progressfinish("warning - some files failed to be removed.\n"); 1492 } else { 1493 progressfinish("succeeded.\n"); 1494 } 1495 1496 progressinfo("Deleting from db... "); 1497 if (dbcalls.delpkg(dbhandle, pinfo.name)) { 1498 progressfinish("failed - perhaps you do not have permissions.\n"); 1499 } else { 1500 progressfinish("succeeded.\n"); 1501 } 1502 1503 if (conf.usescripts == 1) { 1504 progressinfo("Running postremove... "); 1505 1506 scriptrv = runscript(pinfo.postrm, pdef.action, conf.usescripts); 1507 if (scriptrv != 0) { 1508 switch (scriptrv) { 1509 case 1: 1510 progressfinish("failed: I/O error when attempting to run postremove\n"); 1511 break; 1512 case 2: 1513 progressfinish("failed: fork() failed when attempting to run postremove\n"); 1514 break; 1515 case 3: 1516 progressfinish("failed: postremove script returned an error\n"); 1517 break; 1518 } 1519 1520 progressinfo("Warning... "); 1521 progressfinish("postremove failure ignored.\n"); 1522 } else { 1523 progressfinish("succeeded.\n"); 1524 } 1525 } 1526 1527 freepinfo(&pinfo); 1528 signal(2, SIG_DFL); 1529 } 1530 else { 1531 progressfinish("failed: no such package: '%s'.\n", needle.name); 1532 } 1533 } // End of argind for loop 1534 dbcalls.closedb(dbhandle); 1535 } 1536 break; 1537 1538 case 3: // List 1539 dbhandle = NULL; 1540 if ((dbhandle = dbcalls.opendb(conf.dbpath)) == NULL) { 1541 progressfinish("Failed: couldn't open database.\n"); 1542 } 1543 else { 1544 dbcalls.rewinddb(dbhandle); 1545 clearpinfo(&pinfo); 1546 printseparator('-', '.', 2, 25, 50); 1547 printcolumns('|', M_NOBORDERS, M_CENTER, 0, M_CUT, 2, 25, "Name", 50, "Version", "Description"); 1548 printseparator('-', '+', 2, 25, 50); 1549 while (!dbcalls.readpkg(dbhandle, &pinfo)) { 1550 printcolumns('|', M_NOBORDERS, M_LEFT, 1, M_CUT, 2, 25, pinfo.name, 50, pinfo.version, pinfo.description); 1551 freepinfo(&pinfo); 1552 clearpinfo(&pinfo); 1553 1554 } 1555 printseparator('-', '\'', 2, 25, 50); 1556 dbcalls.closedb(dbhandle); 1557 } 1558 break; 1559 1560 case 4: // Find 1561 if (argc < 3) { 1562 Usage(); 1563 dlclose(dbcalls.libhandle); 1564 freeconfig(&conf); 1565 return 1; 1566 } 1567 dbhandle = NULL; 1568 if ((dbhandle = dbcalls.opendb(conf.dbpath)) == NULL) { 1569 progressfinish("Failed: couldn't open database.\n"); 1570 } 1571 else { 1572 dbcalls.rewinddb(dbhandle); 1573 clearpinfo(&needle); 1574 clearpinfo(&pinfo); 1575 1576 printseparator('-', '.', 1, 65); 1577 printcolumns('|', M_NOBORDERS, M_CENTER, 0, M_CUT, 1, 65, "Name", "Version"); 1578 printseparator('-', '+', 1, 65); 1579 1580 for (argind = 2; argind < argc; argind++) { 1581 needle.name = needlelize(argv[argind]); 1582 while (dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_NAME)) { 1583 printcolumns('|', M_NOBORDERS, M_LEFT, 1, M_CUT, 1, 65, pinfo.name, pinfo.version); 1584 freepinfo(&pinfo); 1585 clearpinfo(&pinfo); 1586 } 1587 } 1588 1589 printseparator('-', '\'', 1, 65); 1590 dbcalls.closedb(dbhandle); 1591 free(needle.name); 1592 } 1593 break; 1594 1595 case 5: // Info 1596 if (argc < 3) { 1597 Usage(); 1598 dlclose(dbcalls.libhandle); 1599 freeconfig(&conf); 1600 return 1; 1601 } 1602 dbhandle = NULL; 1603 if ((dbhandle = dbcalls.opendb(conf.dbpath)) == NULL) { 1604 progressfinish("Failed: couldn't open database.\n"); 1605 } 1606 else { 1607 dbcalls.rewinddb(dbhandle); 1608 clearpinfo(&needle); 1609 clearpinfo(&pinfo); 1610 1611 for (argind = 2; argind < argc; argind++) { 1612 needle.name = argv[argind]; 1613 while (dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_NAME)) { 1614 printpinfo(pinfo); 1615 freepinfo(&pinfo); 1616 clearpinfo(&pinfo); 1617 } 1618 } 1619 dbcalls.closedb(dbhandle); 1620 } 1621 break; 1622 1623 case 6: // Find File 1624 if (argc < 3) { 1625 Usage(); 1626 dlclose(dbcalls.libhandle); 1627 freeconfig(&conf); 1628 return 1; 1629 } 1630 dbhandle = NULL; 1631 if ((dbhandle = dbcalls.opendb(conf.dbpath)) == NULL) { 1632 progressfinish("Failed: couldn't open database.\n"); 1633 } 1634 else { 1635 dbcalls.rewinddb(dbhandle); 1636 clearpinfo(&needle); 1637 clearpinfo(&pinfo); 1638 needle.filecount = argc - 2; 1639 needle.files = malloc(sizeof(fileinfo) * needle.filecount); 1640 1641 // Init 1642 for (argind = 2; argind < argc; argind++) { 1643 needle.files[argind-2].name = needlelize(argv[argind]); 1644 needle.files[argind-2].flag = F_IRRELEVANT; 1645 } 1646 1647 printseparator('-', '.', 1, 25); 1648 printcolumns('|', M_NOBORDERS, M_CENTER, 0, M_CUT, 1, 25, "Package", "Filename"); 1649 printseparator('-', '+', 1, 25); 1650 1651 while (dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_FILE)) { 1652 for (i=0; i<pinfo.filecount; i++) { 1653 for (argind = 0; argind < needle.filecount; argind++) { 1654 if (!fnmatch(needle.files[argind].name, pinfo.files[i].name, 0)) { 1655 printcolumns('|', M_NOBORDERS, M_LEFT, 1, M_CUT, 1, 25, pinfo.name, pinfo.files[i].name); 1656 } 1657 } 1658 } 1659 freepinfo(&pinfo); 1660 clearpinfo(&pinfo); 1661 } 1662 1663 printseparator('-', '\'', 1, 25); 1664 1665 // Cleanup 1666 for (argind = 0; argind < needle.filecount; argind++) { 1667 free(needle.files[argind].name); 1668 } 1669 free(needle.files); 1670 dbcalls.closedb(dbhandle); 1671 } 1672 break; 1673 1674 case 7: // List Files 1675 if (argc < 3) { 1676 Usage(); 1677 dlclose(dbcalls.libhandle); 1678 freeconfig(&conf); 1679 return 1; 1680 } 1681 dbhandle = NULL; 1682 if ((dbhandle = dbcalls.opendb(conf.dbpath)) == NULL) { 1683 progressfinish("Failed: couldn't open database.\n"); 1684 } 1685 else { 1686 dbcalls.rewinddb(dbhandle); 1687 clearpinfo(&needle); 1688 clearpinfo(&pinfo); 1689 1690 printseparator('-', '.', 1, 25); 1691 printcolumns('|', M_NOBORDERS, M_CENTER, 0, M_CUT, 1, 25, "Package", "Filename"); 1692 printseparator('-', '+', 1, 25); 1693 for (argind = 2; argind < argc; argind++) { 1694 needle.name = argv[argind]; 1695 if (dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_NAME)) { 1696 do { 1697 for (i=0; i<pinfo.filecount; i++) { 1698 printcolumns('|', M_NOBORDERS, M_LEFT, 1, M_CUT, 1, 25, pinfo.name, pinfo.files[i].name); 1699 } 1700 freepinfo(&pinfo); 1701 clearpinfo(&pinfo); 1702 } while (dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_NAME)); 1703 } 1704 } 1705 printseparator('-', '\'', 1, 25); 1706 1707 dbcalls.closedb(dbhandle); 1708 } 1709 break; 1710 1711 case 8: // Preinfo 1712 if (argc < 3) { 1713 Usage(); 1714 dlclose(dbcalls.libhandle); 1715 freeconfig(&conf); 1716 return 1; 1717 } 1718 1719 for (argind = 2; argind < argc; argind++) { 1720 progressinfo("Preparing... "); 1721 pdef.filepointer = fopen(argv[argind], "r"); 1722 if (pdef.filepointer == NULL) { 1723 progressfinish("failed: couldn't open file.\n"); 1724 dlclose(dbcalls.libhandle); 1725 freeconfig(&conf); 1726 exit(1); 1727 } 1728 progressfinish("succeeded [%d/%d].\n", argind-1, argc-2); 1729 pdef.filename = strdup(argv[argind]); 1730 1731 // Loading proper lib and functions 1732 progressinfo("Identifying package... "); 1733 for (libid=0; conf.pkg_libraries[libid] != NULL; libid++) { 1734 pkgcalls.libhandle = dlopen(conf.pkg_libraries[libid], RTLD_NOW); 1735 if (pkgcalls.libhandle == NULL) { 1736 progressfinish("failed: couldn't open library %s.\ndlopen() error: %s\n", conf.pkg_libraries[libid], dlerror()); 1737 continue; 1738 } 1739 if (loadpkgcalls(&pkgcalls)) { 1740 progressfinish("failed: couldn't load all library functions, library %s.\ndlsym() error: %s\n", conf.pkg_libraries[libid], dlerror()); 1741 continue; 1742 } 1743 if (pkgcalls.identify(pdef) == 0) break; 1744 dlclose(pkgcalls.libhandle); 1745 } 1746 1747 1748 if (conf.pkg_libraries[libid] == NULL) { 1749 progressfinish("failed: unknown package.\n"); 1750 fclose(pdef.filepointer); 1751 free(pdef.filename); 1752 dlclose(dbcalls.libhandle); 1753 freeconfig(&conf); 1754 exit(1); 1755 } 1756 progressfinish("succeeded.\n"); 1757 1758 clearpinfo(&pinfo); 1759 progressinfo("Reading package... "); 1760 pdef.destdir = strdup(conf.instdir); 1761 if (pkgcalls.pkgdetails(pdef, &pinfo)) { 1762 progressfinish("failed: package may be broken.\n"); 1763 fclose(pdef.filepointer); 1764 free(pdef.filename); 1765 free(pdef.destdir); 1766 freepinfo(&pinfo); 1767 dlclose(pkgcalls.libhandle); 1768 break; 1769 } 1770 else { 1771 progressfinish("succeeded.\n"); 1772 } 1773 1774 fclose(pdef.filepointer); 1775 free(pdef.filename); 1776 free(pdef.destdir); 1777 1778 absolutize(&pinfo, conf.instdir); 1779 1780 printseparator('-', '-', 0); 1781 printpinfo(pinfo); 1782 printseparator('-', '-', 0); 1783 for (i=0; i<pinfo.filecount; i++) { 1784 printfilemember(pinfo.name, pinfo.files[i].name); 1785 } 1786 printseparator('-', '-', 0); 1787 freepinfo(&pinfo); 1788 dlclose(pkgcalls.libhandle); 1789 } 1790 break; 1791 1792 case 9: // Execute 1793 if (argc < 4) { 1794 Usage(); 1795 dlclose(dbcalls.libhandle); 1796 freeconfig(&conf); 1797 return 1; 1798 } 1799 1800 if (!strcmp(argv[3], "preinst")) { 1801 rv = 0; 1802 } else if (!strcmp(argv[3], "postinst")) { 1803 rv = 1; 1804 } else if (!strcmp(argv[3], "preremove")) { 1805 rv = 2; 1806 } else if (!strcmp(argv[3], "postremove")) { 1807 rv = 3; 1808 } else { 1809 Usage(); 1810 dlclose(dbcalls.libhandle); 1811 freeconfig(&conf); 1812 return 1; 1813 } 1814 1815 dbhandle = NULL; 1816 progressinfo("Reading package... "); 1817 if ((dbhandle = dbcalls.opendb(conf.dbpath)) == NULL) { 1818 progressfinish("failed: couldn't open database.\n"); 1819 } 1820 else { 1821 dbcalls.rewinddb(dbhandle); 1822 clearpinfo(&needle); 1823 clearpinfo(&pinfo); 1824 1825 needle.name = argv[2]; 1826 1827 if (dbcalls.findpkg(dbhandle, needle, &pinfo, MATCH_NAME)) { 1828 progressfinish("succeeded.\n"); 1829 progressinfo("Executing script... "); 1830 switch (rv) { 1831 case 0: 1832 if (pinfo.preinst.len == 0) { 1833 progressfinish("no such script.\n"); 1834 break; 1835 } 1836 switch (runscript(pinfo.preinst, A_INSTALL, 1)) { 1837 case 0: 1838 progressfinish("succeeded.\n"); 1839 break; 1840 case 1: 1841 progressfinish("failed: I/O error when attempting to run preinstall\n"); 1842 break; 1843 case 2: 1844 progressfinish("failed: fork() failed when attempting to run preinstall\n"); 1845 break; 1846 case 3: 1847 progressfinish("failed: preinstall script returned an error\n"); 1848 break; 1849 } 1850 break; 1851 case 1: 1852 if (pinfo.postinst.len == 0) { 1853 progressfinish("no such script.\n"); 1854 break; 1855 } 1856 switch (runscript(pinfo.postinst, A_INSTALL, 1)) { 1857 case 0: 1858 progressfinish("succeeded.\n"); 1859 break; 1860 case 1: 1861 progressfinish("failed: I/O error when attempting to run postinstall\n"); 1862 break; 1863 case 2: 1864 progressfinish("failed: fork() failed when attempting to run postinstall\n"); 1865 break; 1866 case 3: 1867 progressfinish("failed: postinstall script returned an error\n"); 1868 break; 1869 } 1870 break; 1871 case 2: 1872 if (pinfo.prerm.len == 0) { 1873 progressfinish("no such script.\n"); 1874 break; 1875 } 1876 switch (runscript(pinfo.prerm, A_REMOVE, 1)) { 1877 case 0: 1878 progressfinish("succeeded.\n"); 1879 break; 1880 case 1: 1881 progressfinish("failed: I/O error when attempting to run preremove\n"); 1882 break; 1883 case 2: 1884 progressfinish("failed: fork() failed when attempting to run preremove\n"); 1885 break; 1886 case 3: 1887 progressfinish("failed: preremove script returned an error\n"); 1888 break; 1889 } 1890 break; 1891 case 3: 1892 if (pinfo.postrm.len == 0) { 1893 progressfinish("no such script.\n"); 1894 break; 1895 } 1896 switch (runscript(pinfo.postrm, A_REMOVE, 1)) { 1897 case 0: 1898 progressfinish("succeeded.\n"); 1899 break; 1900 case 1: 1901 progressfinish("failed: I/O error when attempting to run postremove\n"); 1902 break; 1903 case 2: 1904 progressfinish("failed: fork() failed when attempting to run postremove\n"); 1905 break; 1906 case 3: 1907 progressfinish("failed: postremove script returned an error\n"); 1908 break; 1909 } 1910 break; 1911 } 1912 freepinfo(&pinfo); 1913 clearpinfo(&pinfo); 1914 } 1915 else { 1916 progressfinish("No such package.\n"); 1917 } 1918 dbcalls.closedb(dbhandle); 1919 } 1920 break; 1921 1922 default: // Nothing specified; 1923 Usage(); 1924 freeconfig(&conf); 1925 return 1; 1926 break; 1927 } 1928 dlclose(dbcalls.libhandle); 1929 freeconfig(&conf); 1930 return 0; 1931 }