fceffdd95b01a3b995d4b443bfa6135511bb6e93
[oweals/opkg-lede.git] / libopkg / opkg_cmd.c
1 /* opkg_cmd.c - the opkg package management system
2
3    Carl D. Worth
4
5    Copyright (C) 2001 University of Southern California
6
7    This program is free software; you can redistribute it and/or
8    modify it under the terms of the GNU General Public License as
9    published by the Free Software Foundation; either version 2, or (at
10    your option) any later version.
11
12    This program is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    General Public License for more details.
16 */
17
18 #include "config.h"
19
20 #include <stdio.h>
21 #include <dirent.h>
22 #include <glob.h>
23 #include <fnmatch.h>
24 #include <signal.h>
25 #include <unistd.h>
26
27 #include "opkg_conf.h"
28 #include "opkg_cmd.h"
29 #include "opkg_message.h"
30 #include "pkg.h"
31 #include "pkg_dest.h"
32 #include "pkg_parse.h"
33 #include "sprintf_alloc.h"
34 #include "pkg.h"
35 #include "file_util.h"
36 #include "libbb/libbb.h"
37 #include "opkg_utils.h"
38 #include "opkg_defines.h"
39 #include "opkg_download.h"
40 #include "opkg_install.h"
41 #include "opkg_upgrade.h"
42 #include "opkg_remove.h"
43 #include "opkg_configure.h"
44 #include "xsystem.h"
45
46 static void
47 print_pkg(pkg_t *pkg)
48 {
49         char *version = pkg_version_str_alloc(pkg);
50         if (pkg->description)
51                 printf("%s - %s - %s\n", pkg->name, version, pkg->description);
52         else
53                 printf("%s - %s\n", pkg->name, version);
54         free(version);
55 }
56
57 int opkg_state_changed;
58
59 static void
60 write_status_files_if_changed(void)
61 {
62      if (opkg_state_changed && !conf->noaction) {
63           opkg_msg(INFO, "Writing status file.\n");
64           opkg_conf_write_status_files();
65           pkg_write_changed_filelists();
66      } else { 
67           opkg_msg(DEBUG, "Nothing to be done.\n");
68      }
69 }
70
71 static void
72 sigint_handler(int sig)
73 {
74      signal(sig, SIG_DFL);
75      opkg_msg(NOTICE, "Interrupted. Writing out status database.\n");
76      write_status_files_if_changed();
77      exit(128 + sig);
78 }
79
80 static int
81 opkg_update_cmd(int argc, char **argv)
82 {
83      char *tmp;
84      int err;
85      int failures;
86      char *lists_dir;
87      pkg_src_list_elt_t *iter;
88      pkg_src_t *src;
89
90  
91     sprintf_alloc(&lists_dir, "%s", conf->restrict_to_default_dest ? conf->default_dest->lists_dir : conf->lists_dir);
92  
93     if (! file_is_dir(lists_dir)) {
94           if (file_exists(lists_dir)) {
95                opkg_msg(ERROR, "%s exists, but is not a directory.\n",
96                             lists_dir);
97                free(lists_dir);
98                return -1;
99           }
100           err = file_mkdir_hier(lists_dir, 0755);
101           if (err) {
102                free(lists_dir);
103                return -1;
104           }     
105      } 
106
107      failures = 0;
108
109      sprintf_alloc(&tmp, "%s/update-XXXXXX", conf->tmp_dir);
110      if (mkdtemp (tmp) == NULL) {
111          opkg_perror(ERROR, "Failed to make temp dir %s", conf->tmp_dir);
112          return -1;
113      }
114
115
116      for (iter = void_list_first(&conf->pkg_src_list); iter; iter = void_list_next(&conf->pkg_src_list, iter)) {
117           char *url, *list_file_name;
118
119           src = (pkg_src_t *)iter->data;
120
121           if (src->extra_data)  /* debian style? */
122               sprintf_alloc(&url, "%s/%s/%s", src->value, src->extra_data, 
123                             src->gzip ? "Packages.gz" : "Packages");
124           else
125               sprintf_alloc(&url, "%s/%s", src->value, src->gzip ? "Packages.gz" : "Packages");
126
127           sprintf_alloc(&list_file_name, "%s/%s", lists_dir, src->name);
128           if (src->gzip) {
129               char *tmp_file_name;
130               FILE *in, *out;
131               
132               sprintf_alloc (&tmp_file_name, "%s/%s.gz", tmp, src->name);
133               err = opkg_download(url, tmp_file_name, NULL, NULL);
134               if (err == 0) {
135                    opkg_msg(NOTICE, "Inflating %s.\n", url);
136                    in = fopen (tmp_file_name, "r");
137                    out = fopen (list_file_name, "w");
138                    if (in && out)
139                         unzip (in, out);
140                    else
141                         err = 1;
142                    if (in)
143                         fclose (in);
144                    if (out)
145                         fclose (out);
146                    unlink (tmp_file_name);
147               }
148               free(tmp_file_name);
149           } else
150               err = opkg_download(url, list_file_name, NULL, NULL);
151           if (err) {
152                failures++;
153           } else {
154                opkg_msg(NOTICE, "Updated list of available packages in %s.\n",
155                             list_file_name);
156           }
157           free(url);
158 #if defined(HAVE_GPGME) || defined(HAVE_OPENSSL)
159           if (conf->check_signature) {
160               /* download detached signitures to verify the package lists */
161               /* get the url for the sig file */
162               if (src->extra_data)      /* debian style? */
163                   sprintf_alloc(&url, "%s/%s/%s", src->value, src->extra_data,
164                           "Packages.sig");
165               else
166                   sprintf_alloc(&url, "%s/%s", src->value, "Packages.sig");
167
168               /* create temporary file for it */
169               char *tmp_file_name;
170
171               /* Put the signature in the right place */
172               sprintf_alloc (&tmp_file_name, "%s/%s.sig", lists_dir, src->name);
173
174               err = opkg_download(url, tmp_file_name, NULL, NULL);
175               if (err) {
176                   failures++;
177                   opkg_msg(NOTICE, "Signature check failed.\n");
178               } else {
179                   err = opkg_verify_file (list_file_name, tmp_file_name);
180                   if (err == 0)
181                       opkg_msg(NOTICE, "Signature check passed.\n");
182                   else
183                       opkg_msg(NOTICE, "Signature check failed.\n");
184               }
185               /* We shouldn't unlink the signature ! */
186               // unlink (tmp_file_name);
187               free (tmp_file_name);
188               free (url);
189           }
190 #else
191           // Do nothing
192 #endif
193           free(list_file_name);
194      }
195      rmdir (tmp);
196      free (tmp);
197      free(lists_dir);
198
199      return failures;
200 }
201
202
203 struct opkg_intercept
204 {
205     char *oldpath;
206     char *statedir;
207 };
208
209 typedef struct opkg_intercept *opkg_intercept_t;
210
211 static opkg_intercept_t
212 opkg_prep_intercepts(void)
213 {
214     opkg_intercept_t ctx;
215     char *newpath;
216
217     ctx = xcalloc(1, sizeof (*ctx));
218     ctx->oldpath = xstrdup(getenv("PATH"));
219     sprintf_alloc(&newpath, "%s/opkg/intercept:%s", DATADIR, ctx->oldpath);
220     sprintf_alloc(&ctx->statedir, "%s/opkg-intercept-XXXXXX", conf->tmp_dir);
221
222     if (mkdtemp(ctx->statedir) == NULL) {
223         opkg_perror(ERROR,"Failed to make temp dir %s", ctx->statedir);
224         free(ctx->oldpath);
225         free(ctx->statedir);
226         free(newpath);
227         free(ctx);
228         return NULL;
229     }
230
231     setenv("OPKG_INTERCEPT_DIR", ctx->statedir, 1);
232     setenv("PATH", newpath, 1);
233     free(newpath);
234
235     return ctx;
236 }
237
238 static int
239 opkg_finalize_intercepts(opkg_intercept_t ctx)
240 {
241     DIR *dir;
242     int err = 0;
243
244     setenv ("PATH", ctx->oldpath, 1);
245     free (ctx->oldpath);
246
247     dir = opendir (ctx->statedir);
248     if (dir) {
249         struct dirent *de;
250         while (de = readdir (dir), de != NULL) {
251             char *path;
252
253             if (de->d_name[0] == '.')
254                 continue;
255             
256             sprintf_alloc (&path, "%s/%s", ctx->statedir, de->d_name);
257             if (access (path, X_OK) == 0) {
258                 const char *argv[] = {"sh", "-c", path, NULL};
259                 xsystem (argv);
260             }
261             free (path);
262         }
263         closedir(dir);
264     } else
265         opkg_perror(ERROR, "Failed to open dir %s", ctx->statedir);
266         
267     rm_r(ctx->statedir);
268     free (ctx->statedir);
269     free (ctx);
270
271     return err;
272 }
273
274 /* For package pkg do the following: If it is already visited, return. If not,
275    add it in visited list and recurse to its deps. Finally, add it to ordered 
276    list.
277    pkg_vec all contains all available packages in repos.
278    pkg_vec visited contains packages already visited by this function, and is 
279    used to end recursion and avoid an infinite loop on graph cycles.
280    pkg_vec ordered will finally contain the ordered set of packages.
281 */
282 static int
283 opkg_recurse_pkgs_in_order(pkg_t *pkg, pkg_vec_t *all,
284                                pkg_vec_t *visited, pkg_vec_t *ordered)
285 {
286     int j,k,l,m;
287     int count;
288     pkg_t *dep;
289     compound_depend_t * compound_depend;
290     depend_t ** possible_satisfiers;
291     abstract_pkg_t *abpkg;
292     abstract_pkg_t **dependents;
293
294     /* If it's just an available package, that is, not installed and not even
295        unpacked, skip it */
296     /* XXX: This is probably an overkill, since a state_status != SS_UNPACKED 
297        would do here. However, if there is an intermediate node (pkg) that is 
298        configured and installed between two unpacked packages, the latter 
299        won't be properly reordered, unless all installed/unpacked pkgs are
300        checked */
301     if (pkg->state_status == SS_NOT_INSTALLED) 
302         return 0;
303
304     /* If the  package has already been visited (by this function), skip it */
305     for(j = 0; j < visited->len; j++) 
306         if ( ! strcmp(visited->pkgs[j]->name, pkg->name)) {
307             opkg_msg(DEBUG, "pkg %s already visited, skipping.\n", pkg->name);
308             return 0;
309         }
310     
311     pkg_vec_insert(visited, pkg);
312
313     count = pkg->pre_depends_count + pkg->depends_count + \
314         pkg->recommends_count + pkg->suggests_count;
315
316     opkg_msg(DEBUG, "pkg %s.\n", pkg->name);
317
318     /* Iterate over all the dependencies of pkg. For each one, find a package 
319        that is either installed or unpacked and satisfies this dependency.
320        (there should only be one such package per dependency installed or 
321        unpacked). Then recurse to the dependency package */
322     for (j=0; j < count ; j++) {
323         compound_depend = &pkg->depends[j];
324         possible_satisfiers = compound_depend->possibilities;
325         for (k=0; k < compound_depend->possibility_count ; k++) {
326             abpkg = possible_satisfiers[k]->pkg;
327             dependents = abpkg->provided_by->pkgs;
328             l = 0;
329             if (dependents != NULL)
330                 while (l < abpkg->provided_by->len && dependents[l] != NULL) {
331                     opkg_msg(DEBUG, "Descending on pkg %s.\n", 
332                                  dependents [l]->name);
333     
334                     /* find whether dependent l is installed or unpacked,
335                      * and then find which package in the list satisfies it */
336                     for(m = 0; m < all->len; m++) {
337                         dep = all->pkgs[m];
338                         if ( dep->state_status != SS_NOT_INSTALLED)
339                             if ( ! strcmp(dep->name, dependents[l]->name)) {
340                                 opkg_recurse_pkgs_in_order(dep, all, 
341                                                            visited, ordered);
342                                 /* Stop the outer loop */
343                                 l = abpkg->provided_by->len;
344                                 /* break from the inner loop */
345                                 break;
346                             }
347                     }
348                     l++;
349                 }
350         }
351     }
352
353     /* When all recursions from this node down, are over, and all 
354        dependencies have been added in proper order in the ordered array, add
355        also the package pkg to ordered array */
356     pkg_vec_insert(ordered, pkg);
357
358     return 0;
359
360 }
361
362 static int
363 opkg_configure_packages(char *pkg_name)
364 {
365      pkg_vec_t *all, *ordered, *visited;
366      int i;
367      pkg_t *pkg;
368      opkg_intercept_t ic;
369      int r, err = 0;
370
371      opkg_msg(INFO, "Configuring unpacked packages.\n");
372
373      all = pkg_vec_alloc();
374
375      pkg_hash_fetch_available(all);
376
377      /* Reorder pkgs in order to be configured according to the Depends: tag
378         order */
379      opkg_msg(INFO, "Reordering packages before configuring them...\n");
380      ordered = pkg_vec_alloc();
381      visited = pkg_vec_alloc();
382      for(i = 0; i < all->len; i++) {
383          pkg = all->pkgs[i];
384          opkg_recurse_pkgs_in_order(pkg, all, visited, ordered);
385      }
386
387      ic = opkg_prep_intercepts();
388      if (ic == NULL) {
389              err = -1;
390              goto error;
391      }
392     
393      for(i = 0; i < all->len; i++) {
394           pkg = all->pkgs[i];
395
396           if (pkg_name && fnmatch(pkg_name, pkg->name, 0)) 
397                continue;
398
399           if (pkg->state_status == SS_UNPACKED) {
400                opkg_msg(NOTICE, "Configuring %s.\n", pkg->name);
401                r = opkg_configure(pkg);
402                if (r == 0) {
403                     pkg->state_status = SS_INSTALLED;
404                     pkg->parent->state_status = SS_INSTALLED;
405                     pkg->state_flag &= ~SF_PREFER;
406                     opkg_state_changed++;
407                } else {
408                     if (!err)
409                         err = r;
410                }
411           }
412      }
413
414      r = opkg_finalize_intercepts (ic);
415      if (r && !err)
416          err = r;
417
418 error:
419      pkg_vec_free(all);
420      pkg_vec_free(ordered);
421      pkg_vec_free(visited);
422
423      return err;
424 }
425
426 static int
427 opkg_install_cmd(int argc, char **argv)
428 {
429      int i;
430      char *arg;
431      int err=0;
432
433      signal(SIGINT, sigint_handler);
434
435      /*
436       * Now scan through package names and install
437       */
438      for (i=0; i < argc; i++) {
439           arg = argv[i];
440
441           opkg_msg(DEBUG2, "%s\n", arg);
442           err = opkg_prepare_url_for_install(arg, &argv[i]);
443           if (err)
444               return err;
445      }
446      pkg_info_preinstall_check();
447
448      for (i=0; i < argc; i++) {
449           arg = argv[i];
450           err = opkg_install_by_name(arg);
451           if (err) {
452                opkg_msg(ERROR, "Cannot install package %s.\n", arg);
453           }
454      }
455
456      opkg_configure_packages(NULL);
457
458      write_status_files_if_changed();
459
460      return err;
461 }
462
463 static int
464 opkg_upgrade_cmd(int argc, char **argv)
465 {
466      int i;
467      pkg_t *pkg;
468      int err;
469
470      signal(SIGINT, sigint_handler);
471
472      if (argc) {
473           for (i=0; i < argc; i++) {
474                char *arg = argv[i];
475
476                err = opkg_prepare_url_for_install(arg, &arg);
477                if (err)
478                    return err;
479           }
480           pkg_info_preinstall_check();
481
482           for (i=0; i < argc; i++) {
483                char *arg = argv[i];
484                if (conf->restrict_to_default_dest) {
485                     pkg = pkg_hash_fetch_installed_by_name_dest(argv[i],
486                                                         conf->default_dest);
487                     if (pkg == NULL) {
488                          opkg_msg(NOTICE, "Package %s not installed in %s.\n",
489                                       argv[i], conf->default_dest->name);
490                          continue;
491                     }
492                } else {
493                     pkg = pkg_hash_fetch_installed_by_name(argv[i]);
494                }
495                if (pkg)
496                     opkg_upgrade_pkg(pkg);
497                else {
498                     opkg_install_by_name(arg);
499                }
500           }
501      } else {
502           pkg_vec_t *installed = pkg_vec_alloc();
503
504           pkg_info_preinstall_check();
505
506           pkg_hash_fetch_all_installed(installed);
507           for (i = 0; i < installed->len; i++) {
508                pkg = installed->pkgs[i];
509                opkg_upgrade_pkg(pkg);
510           }
511           pkg_vec_free(installed);
512      }
513
514      opkg_configure_packages(NULL);
515
516      write_status_files_if_changed();
517
518      return 0;
519 }
520
521 static int
522 opkg_download_cmd(int argc, char **argv)
523 {
524      int i, err;
525      char *arg;
526      pkg_t *pkg;
527
528      pkg_info_preinstall_check();
529      for (i = 0; i < argc; i++) {
530           arg = argv[i];
531
532           pkg = pkg_hash_fetch_best_installation_candidate_by_name(arg);
533           if (pkg == NULL) {
534                opkg_msg(ERROR, "Cannot find package %s.\n", arg);
535                continue;
536           }
537
538           err = opkg_download_pkg(pkg, ".");
539
540           if (err) {
541                opkg_msg(ERROR, "Failed to download %s.\n", pkg->name);
542           } else {
543                opkg_msg(NOTICE, "Downloaded %s as %s.\n",
544                             pkg->name, pkg->local_filename);
545           }
546      }
547
548      return 0;
549 }
550
551
552 static int
553 opkg_list_cmd(int argc, char **argv)
554 {
555      int i;
556      pkg_vec_t *available;
557      pkg_t *pkg;
558      char *pkg_name = NULL;
559
560      if (argc > 0) {
561           pkg_name = argv[0];
562      }
563      available = pkg_vec_alloc();
564      pkg_hash_fetch_available(available);
565      pkg_vec_sort(available, pkg_compare_names);
566      for (i=0; i < available->len; i++) {
567           pkg = available->pkgs[i];
568           /* if we have package name or pattern and pkg does not match, then skip it */
569           if (pkg_name && fnmatch(pkg_name, pkg->name, 0)) 
570                continue;
571           print_pkg(pkg);
572      }
573      pkg_vec_free(available);
574
575      return 0;
576 }
577
578
579 static int
580 opkg_list_installed_cmd(int argc, char **argv)
581 {
582      int i ;
583      pkg_vec_t *available;
584      pkg_t *pkg;
585      char *pkg_name = NULL;
586
587      if (argc > 0) {
588           pkg_name = argv[0];
589      }
590      available = pkg_vec_alloc();
591      pkg_hash_fetch_all_installed(available);
592      pkg_vec_sort(available, pkg_compare_names);
593      for (i=0; i < available->len; i++) {
594           pkg = available->pkgs[i];
595           /* if we have package name or pattern and pkg does not match, then skip it */
596           if (pkg_name && fnmatch(pkg_name, pkg->name, 0)) 
597                continue;
598           print_pkg(pkg);
599      }
600
601      pkg_vec_free(available);
602
603      return 0;
604 }
605
606 static int
607 opkg_list_upgradable_cmd(int argc, char **argv)
608 {
609     struct active_list *head = prepare_upgrade_list();
610     struct active_list *node=NULL;
611     pkg_t *_old_pkg, *_new_pkg;
612     char *old_v, *new_v;
613     for (node = active_list_next(head, head); node;node = active_list_next(head,node)) {
614         _old_pkg = list_entry(node, pkg_t, list);
615         _new_pkg = pkg_hash_fetch_best_installation_candidate_by_name(_old_pkg->name);
616         if (_new_pkg == NULL)
617                 continue;
618         old_v = pkg_version_str_alloc(_old_pkg);
619         new_v = pkg_version_str_alloc(_new_pkg);
620         printf("%s - %s - %s\n", _old_pkg->name, old_v, new_v);
621         free(old_v);
622         free(new_v);
623     }
624     active_list_head_delete(head);
625     return 0;
626 }
627
628 static int
629 opkg_info_status_cmd(int argc, char **argv, int installed_only)
630 {
631      int i;
632      pkg_vec_t *available;
633      pkg_t *pkg;
634      char *pkg_name = NULL;
635
636      if (argc > 0) {
637           pkg_name = argv[0];
638      }
639
640      available = pkg_vec_alloc();
641      if (installed_only)
642           pkg_hash_fetch_all_installed(available);
643      else
644           pkg_hash_fetch_available(available);
645
646      for (i=0; i < available->len; i++) {
647           pkg = available->pkgs[i];
648           if (pkg_name && fnmatch(pkg_name, pkg->name, 0)) {
649                continue;
650           }
651
652           pkg_formatted_info(stdout, pkg);
653
654           if (conf->verbosity >= NOTICE) {
655                conffile_list_elt_t *iter;
656                for (iter = nv_pair_list_first(&pkg->conffiles); iter; iter = nv_pair_list_next(&pkg->conffiles, iter)) {
657                     conffile_t *cf = (conffile_t *)iter->data;
658                     int modified = conffile_has_been_modified(cf);
659                     if (cf->value)
660                         opkg_msg(INFO, "conffile=%s md5sum=%s modified=%d.\n",
661                                  cf->name, cf->value, modified);
662                }
663           }
664      }
665      pkg_vec_free(available);
666
667      return 0;
668 }
669
670 static int
671 opkg_info_cmd(int argc, char **argv)
672 {
673      return opkg_info_status_cmd(argc, argv, 0);
674 }
675
676 static int
677 opkg_status_cmd(int argc, char **argv)
678 {
679      return opkg_info_status_cmd(argc, argv, 1);
680 }
681
682 static int
683 opkg_configure_cmd(int argc, char **argv)
684 {
685         int err;
686         char *pkg_name = NULL;
687
688         if (argc > 0)
689                 pkg_name = argv[0];
690
691         err = opkg_configure_packages(pkg_name);
692
693         write_status_files_if_changed();
694
695         return err;
696 }
697
698 static int
699 opkg_remove_cmd(int argc, char **argv)
700 {
701      int i, a, done;
702      pkg_t *pkg;
703      pkg_t *pkg_to_remove;
704      pkg_vec_t *available;
705
706      done = 0;
707
708      signal(SIGINT, sigint_handler);
709
710      pkg_info_preinstall_check();
711
712      available = pkg_vec_alloc();
713      pkg_hash_fetch_all_installed(available);
714
715      for (i=0; i<argc; i++) {
716         for (a=0; a<available->len; a++) {
717             pkg = available->pkgs[a];
718             if (fnmatch(argv[i], pkg->name, 0)) {
719                continue;
720             }
721             if (conf->restrict_to_default_dest) {
722                  pkg_to_remove = pkg_hash_fetch_installed_by_name_dest(
723                                         pkg->name,
724                                         conf->default_dest);
725             } else {
726                  pkg_to_remove = pkg_hash_fetch_installed_by_name(pkg->name);
727             }
728         
729             if (pkg_to_remove == NULL) {
730                  opkg_msg(ERROR, "Package %s is not installed.\n", pkg->name);
731                  continue;
732             }
733             if (pkg->state_status == SS_NOT_INSTALLED) {    // Added the control, so every already removed package could be skipped
734                  opkg_msg(ERROR, "Package %s not installed.\n", pkg->name);
735                  continue;
736             }
737             opkg_remove_pkg(pkg_to_remove, 0);
738             done = 1;
739         }
740      }
741
742      pkg_vec_free(available);
743
744      if (done == 0)
745         opkg_msg(NOTICE, "No packages removed.\n");
746
747      write_status_files_if_changed();
748      return 0;
749 }
750
751 static int
752 opkg_flag_cmd(int argc, char **argv)
753 {
754      int i;
755      pkg_t *pkg;
756      const char *flags = argv[0];
757     
758      signal(SIGINT, sigint_handler);
759
760      for (i=1; i < argc; i++) {
761           if (conf->restrict_to_default_dest) {
762                pkg = pkg_hash_fetch_installed_by_name_dest(argv[i],
763                                                            conf->default_dest);
764           } else {
765                pkg = pkg_hash_fetch_installed_by_name(argv[i]);
766           }
767
768           if (pkg == NULL) {
769                opkg_msg(ERROR, "Package %s is not installed.\n", argv[i]);
770                continue;
771           }
772           if (( strcmp(flags,"hold")==0)||( strcmp(flags,"noprune")==0)||
773               ( strcmp(flags,"user")==0)||( strcmp(flags,"ok")==0)) {
774               pkg->state_flag = pkg_state_flag_from_str(flags);
775           }
776
777           /* 
778            * Useful if a package is installed in an offline_root, and
779            * should be configured by opkg-cl configure at a later date.
780            */
781           if (( strcmp(flags,"installed")==0)||( strcmp(flags,"unpacked")==0)){
782               pkg->state_status = pkg_state_status_from_str(flags);
783           }
784
785           opkg_state_changed++;
786           opkg_msg(NOTICE, "Setting flags for package %s to %s.\n",
787                        pkg->name, flags);
788      }
789
790      write_status_files_if_changed();
791      return 0;
792 }
793
794 static int
795 opkg_files_cmd(int argc, char **argv)
796 {
797      pkg_t *pkg;
798      str_list_t *files;
799      str_list_elt_t *iter;
800      char *pkg_version;
801
802      if (argc < 1) {
803           return -1;
804      }
805
806      pkg = pkg_hash_fetch_installed_by_name(argv[0]);
807      if (pkg == NULL) {
808           opkg_msg(ERROR, "Package %s not installed.\n", argv[0]);
809           return 0;
810      }
811
812      files = pkg_get_installed_files(pkg);
813      pkg_version = pkg_version_str_alloc(pkg);
814
815      printf("Package %s (%s) is installed on %s and has the following files:\n",
816                 pkg->name, pkg_version, pkg->dest->name);
817
818      for (iter=str_list_first(files); iter; iter=str_list_next(files, iter))
819           printf("%s\n", (char *)iter->data);
820
821      free(pkg_version);
822      pkg_free_installed_files(pkg);
823
824      return 0;
825 }
826
827 static int
828 opkg_depends_cmd(int argc, char **argv)
829 {
830         int i, j, k;
831         int depends_count;
832         pkg_vec_t *available_pkgs;
833         compound_depend_t *cdep;
834         pkg_t *pkg;
835         char *str;
836
837         pkg_info_preinstall_check();
838
839         available_pkgs = pkg_vec_alloc();
840         if (conf->query_all)
841                pkg_hash_fetch_available(available_pkgs);
842         else
843                pkg_hash_fetch_all_installed(available_pkgs);
844
845         for (i=0; i<argc; i++) {
846                 for (j=0; j<available_pkgs->len; j++) {
847                         pkg = available_pkgs->pkgs[j];
848
849                         if (fnmatch(argv[i], pkg->name, 0) != 0)
850                                 continue;
851
852                         depends_count = pkg->depends_count +
853                                         pkg->pre_depends_count +
854                                         pkg->recommends_count +
855                                         pkg->suggests_count;
856
857                         opkg_msg(NOTICE, "%s depends on:\n", pkg->name);
858
859                         for (k=0; k<depends_count; k++) {
860                                 cdep = &pkg->depends[k];
861
862                                 if (cdep->type != DEPEND)
863                                       continue;
864
865                                 str = pkg_depend_str(pkg, k);
866                                 opkg_msg(NOTICE, "\t%s\n", str);
867                                 free(str);
868                         }
869
870                }
871         }
872
873         pkg_vec_free(available_pkgs);
874         return 0;
875 }
876
877 static int
878 pkg_mark_provides(pkg_t *pkg)
879 {
880      int provides_count = pkg->provides_count;
881      abstract_pkg_t **provides = pkg->provides;
882      int i;
883      pkg->parent->state_flag |= SF_MARKED;
884      for (i = 0; i < provides_count; i++) {
885           provides[i]->state_flag |= SF_MARKED;
886      }
887      return 0;
888 }
889
890 enum what_field_type {
891   WHATDEPENDS,
892   WHATCONFLICTS,
893   WHATPROVIDES,
894   WHATREPLACES,
895   WHATRECOMMENDS,
896   WHATSUGGESTS
897 };
898
899 static int
900 opkg_what_depends_conflicts_cmd(enum depend_type what_field_type, int recursive, int argc, char **argv)
901 {
902         depend_t *possibility;
903         compound_depend_t *cdep;
904         pkg_vec_t *available_pkgs;
905         pkg_t *pkg;
906         int i, j, k, l;
907         int changed, count;
908         const char *rel_str = NULL;
909         char *ver;
910
911         switch (what_field_type) {
912         case DEPEND: rel_str = "depends on"; break;
913         case CONFLICTS: rel_str = "conflicts with"; break;
914         case SUGGEST: rel_str = "suggests"; break;
915         case RECOMMEND: rel_str = "recommends"; break;
916         default: return -1;
917         }
918      
919         available_pkgs = pkg_vec_alloc();
920
921         if (conf->query_all)
922                pkg_hash_fetch_available(available_pkgs);
923         else
924                pkg_hash_fetch_all_installed(available_pkgs);
925
926         /* mark the root set */
927         pkg_vec_clear_marks(available_pkgs);
928         opkg_msg(NOTICE, "Root set:\n");
929         for (i = 0; i < argc; i++)
930                pkg_vec_mark_if_matches(available_pkgs, argv[i]);
931
932         for (i = 0; i < available_pkgs->len; i++) {
933                pkg = available_pkgs->pkgs[i];
934                if (pkg->state_flag & SF_MARKED) {
935                     /* mark the parent (abstract) package */
936                     pkg_mark_provides(pkg);
937                     opkg_msg(NOTICE, "  %s\n", pkg->name);
938                }
939         }
940
941         opkg_msg(NOTICE, "What %s root set\n", rel_str);
942         do {
943                 changed = 0;
944
945                 for (j=0; j<available_pkgs->len; j++) {
946
947                         pkg = available_pkgs->pkgs[j];
948                         count = ((what_field_type == CONFLICTS)
949                                  ? pkg->conflicts_count
950                                  : pkg->pre_depends_count +
951                                  pkg->depends_count +
952                                  pkg->recommends_count +
953                                  pkg->suggests_count);
954
955                         /* skip this package if it is already marked */
956                         if (pkg->parent->state_flag & SF_MARKED)
957                                 continue;
958
959                         for (k=0; k<count; k++) {
960                                 cdep = (what_field_type == CONFLICTS)
961                                         ? &pkg->conflicts[k]
962                                         : &pkg->depends[k];
963
964                                 if (what_field_type != cdep->type)
965                                         continue;
966
967                                 for (l=0; l<cdep->possibility_count; l++) {
968                                         possibility = cdep->possibilities[l];
969
970                                         if ((possibility->pkg->state_flag
971                                                                 & SF_MARKED)
972                                                         != SF_MARKED)
973                                                 continue;
974                                         
975                                         /* mark the depending package so we
976                                         * won't visit it again */
977                                         pkg->state_flag |= SF_MARKED;
978                                         pkg_mark_provides(pkg);
979                                         changed++;
980
981                                         ver = pkg_version_str_alloc(pkg); 
982                                         opkg_msg(NOTICE, "\t%s %s\t%s %s",
983                                                         pkg->name,
984                                                         ver,
985                                                         rel_str,
986                                                         possibility->pkg->name);
987                                         free(ver);
988                                         if (possibility->version) {
989                                                 opkg_msg(NOTICE, " (%s%s)",
990                                                         constraint_to_str(possibility->constraint),
991                                                         possibility->version);
992                                         }
993                                         if (!pkg_dependence_satisfiable(possibility))
994                                                 opkg_msg(NOTICE,
995                                                         " unsatisfiable");
996                                         opkg_msg(NOTICE, "\n");
997                                         goto next_package;
998                                 }
999                         }
1000 next_package:
1001                         ;
1002                 }
1003         } while (changed && recursive);
1004
1005         pkg_vec_free(available_pkgs);
1006
1007         return 0;
1008 }
1009
1010 static int
1011 opkg_whatdepends_recursively_cmd(int argc, char **argv)
1012 {
1013      return opkg_what_depends_conflicts_cmd(DEPEND, 1, argc, argv);
1014 }
1015
1016 static int
1017 opkg_whatdepends_cmd(int argc, char **argv)
1018 {
1019      return opkg_what_depends_conflicts_cmd(DEPEND, 0, argc, argv);
1020 }
1021
1022 static int
1023 opkg_whatsuggests_cmd(int argc, char **argv)
1024 {
1025      return opkg_what_depends_conflicts_cmd(SUGGEST, 0, argc, argv);
1026 }
1027
1028 static int
1029 opkg_whatrecommends_cmd(int argc, char **argv)
1030 {
1031      return opkg_what_depends_conflicts_cmd(RECOMMEND, 0, argc, argv);
1032 }
1033
1034 static int
1035 opkg_whatconflicts_cmd(int argc, char **argv)
1036 {
1037      return opkg_what_depends_conflicts_cmd(CONFLICTS, 0, argc, argv);
1038 }
1039
1040 static int
1041 opkg_what_provides_replaces_cmd(enum what_field_type what_field_type, int argc, char **argv)
1042 {
1043
1044      if (argc > 0) {
1045           pkg_vec_t *available_pkgs = pkg_vec_alloc();
1046           const char *rel_str = (what_field_type == WHATPROVIDES ? "provides" : "replaces");
1047           int i;
1048      
1049           pkg_info_preinstall_check();
1050
1051           if (conf->query_all)
1052                pkg_hash_fetch_available(available_pkgs);
1053           else
1054                pkg_hash_fetch_all_installed(available_pkgs);
1055           for (i = 0; i < argc; i++) {
1056                const char *target = argv[i];
1057                int j;
1058
1059                opkg_msg(NOTICE, "What %s %s\n",
1060                             rel_str, target);
1061                for (j = 0; j < available_pkgs->len; j++) {
1062                     pkg_t *pkg = available_pkgs->pkgs[j];
1063                     int k;
1064                     int count = (what_field_type == WHATPROVIDES) ? pkg->provides_count : pkg->replaces_count;
1065                     for (k = 0; k < count; k++) {
1066                          abstract_pkg_t *apkg = 
1067                               ((what_field_type == WHATPROVIDES) 
1068                                ? pkg->provides[k]
1069                                : pkg->replaces[k]);
1070                          if (fnmatch(target, apkg->name, 0) == 0) {
1071                               opkg_msg(NOTICE, "    %s", pkg->name);
1072                               if (strcmp(target, apkg->name) != 0)
1073                                    opkg_msg(NOTICE, "\t%s %s\n",
1074                                                    rel_str, apkg->name);
1075                               opkg_msg(NOTICE, "\n");
1076                          }
1077                     }
1078                }
1079           }
1080           pkg_vec_free(available_pkgs);
1081      }
1082      return 0;
1083 }
1084
1085 static int
1086 opkg_whatprovides_cmd(int argc, char **argv)
1087 {
1088      return opkg_what_provides_replaces_cmd(WHATPROVIDES, argc, argv);
1089 }
1090
1091 static int
1092 opkg_whatreplaces_cmd(int argc, char **argv)
1093 {
1094      return opkg_what_provides_replaces_cmd(WHATREPLACES, argc, argv);
1095 }
1096
1097 static int
1098 opkg_search_cmd(int argc, char **argv)
1099 {
1100      int i;
1101
1102      pkg_vec_t *installed;
1103      pkg_t *pkg;
1104      str_list_t *installed_files;
1105      str_list_elt_t *iter;
1106      char *installed_file;
1107
1108      if (argc < 1) {
1109           return -1;
1110      }
1111  
1112      installed = pkg_vec_alloc();
1113      pkg_hash_fetch_all_installed(installed);
1114      pkg_vec_sort(installed, pkg_compare_names);
1115
1116      for (i=0; i < installed->len; i++) {
1117           pkg = installed->pkgs[i];
1118
1119           installed_files = pkg_get_installed_files(pkg);
1120
1121           for (iter = str_list_first(installed_files); iter; iter = str_list_next(installed_files, iter)) {
1122                installed_file = (char *)iter->data;
1123                if (fnmatch(argv[0], installed_file, 0)==0)
1124                     print_pkg(pkg);
1125           }
1126
1127           pkg_free_installed_files(pkg);
1128      }
1129
1130      pkg_vec_free(installed);
1131
1132      return 0;
1133 }
1134
1135 static int
1136 opkg_compare_versions_cmd(int argc, char **argv)
1137 {
1138      if (argc == 3) {
1139           /* this is a bit gross */
1140           struct pkg p1, p2;
1141           parse_version(&p1, argv[0]); 
1142           parse_version(&p2, argv[2]); 
1143           return pkg_version_satisfied(&p1, &p2, argv[1]);
1144      } else {
1145           opkg_msg(ERROR,
1146                        "opkg compare_versions <v1> <op> <v2>\n"
1147                        "<op> is one of <= >= << >> =\n");
1148           return -1;
1149      }
1150 }
1151
1152 static int
1153 opkg_print_architecture_cmd(int argc, char **argv)
1154 {
1155      nv_pair_list_elt_t *l;
1156
1157      list_for_each_entry(l, &conf->arch_list.head, node) {
1158           nv_pair_t *nv = (nv_pair_t *)l->data;
1159           printf("arch %s %s\n", nv->name, nv->value);
1160      }
1161      return 0;
1162 }
1163
1164
1165 /* XXX: CLEANUP: The usage strings should be incorporated into this
1166    array for easier maintenance */
1167 static opkg_cmd_t cmds[] = {
1168      {"update", 0, (opkg_cmd_fun_t)opkg_update_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1169      {"upgrade", 0, (opkg_cmd_fun_t)opkg_upgrade_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1170      {"list", 0, (opkg_cmd_fun_t)opkg_list_cmd, PFM_SOURCE},
1171      {"list_installed", 0, (opkg_cmd_fun_t)opkg_list_installed_cmd, PFM_SOURCE},
1172      {"list-installed", 0, (opkg_cmd_fun_t)opkg_list_installed_cmd, PFM_SOURCE},
1173      {"list_upgradable", 0, (opkg_cmd_fun_t)opkg_list_upgradable_cmd, PFM_SOURCE},
1174      {"list-upgradable", 0, (opkg_cmd_fun_t)opkg_list_upgradable_cmd, PFM_SOURCE},
1175      {"info", 0, (opkg_cmd_fun_t)opkg_info_cmd, 0},
1176      {"flag", 1, (opkg_cmd_fun_t)opkg_flag_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1177      {"status", 0, (opkg_cmd_fun_t)opkg_status_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1178      {"install", 1, (opkg_cmd_fun_t)opkg_install_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1179      {"remove", 1, (opkg_cmd_fun_t)opkg_remove_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1180      {"configure", 0, (opkg_cmd_fun_t)opkg_configure_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1181      {"files", 1, (opkg_cmd_fun_t)opkg_files_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1182      {"search", 1, (opkg_cmd_fun_t)opkg_search_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1183      {"download", 1, (opkg_cmd_fun_t)opkg_download_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1184      {"compare_versions", 1, (opkg_cmd_fun_t)opkg_compare_versions_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1185      {"compare-versions", 1, (opkg_cmd_fun_t)opkg_compare_versions_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1186      {"print-architecture", 0, (opkg_cmd_fun_t)opkg_print_architecture_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1187      {"print_architecture", 0, (opkg_cmd_fun_t)opkg_print_architecture_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1188      {"print-installation-architecture", 0, (opkg_cmd_fun_t)opkg_print_architecture_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1189      {"print_installation_architecture", 0, (opkg_cmd_fun_t)opkg_print_architecture_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1190      {"depends", 1, (opkg_cmd_fun_t)opkg_depends_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1191      {"whatdepends", 1, (opkg_cmd_fun_t)opkg_whatdepends_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1192      {"whatdependsrec", 1, (opkg_cmd_fun_t)opkg_whatdepends_recursively_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1193      {"whatrecommends", 1, (opkg_cmd_fun_t)opkg_whatrecommends_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1194      {"whatsuggests", 1, (opkg_cmd_fun_t)opkg_whatsuggests_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1195      {"whatprovides", 1, (opkg_cmd_fun_t)opkg_whatprovides_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1196      {"whatreplaces", 1, (opkg_cmd_fun_t)opkg_whatreplaces_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1197      {"whatconflicts", 1, (opkg_cmd_fun_t)opkg_whatconflicts_cmd, PFM_DESCRIPTION|PFM_SOURCE},
1198 };
1199
1200 opkg_cmd_t *
1201 opkg_cmd_find(const char *name)
1202 {
1203         int i;
1204         opkg_cmd_t *cmd;
1205         int num_cmds = sizeof(cmds) / sizeof(opkg_cmd_t);
1206
1207         for (i=0; i < num_cmds; i++) {
1208                 cmd = &cmds[i];
1209                 if (strcmp(name, cmd->name) == 0)
1210                         return cmd;
1211         }
1212
1213         return NULL;
1214 }
1215
1216 int
1217 opkg_cmd_exec(opkg_cmd_t *cmd, int argc, const char **argv)
1218 {
1219         return (cmd->fun)(argc, argv);
1220 }