"Fossies" - the Fresh Open Source Software Archive

Member "unipkg-0.6.5/unipkg.c" (16 Dec 2005, 58293 Bytes) of package /linux/privat/old/unipkg-0.6.5.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

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(&timestamp, &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 }