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