3a662d2b24dff2806b53b2b6e43f7756fdc0095e
[oweals/opkg-lede.git] / libopkg / opkg.c
1 /* opkg.c - the opkg  package management system
2
3    Thomas Wood <thomas@openedhand.com>
4
5    Copyright (C) 2008 OpenMoko Inc
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 #include <fnmatch.h>
20
21 #include "opkg.h"
22 #include "opkg_conf.h"
23 #include "args.h"
24
25 #include "opkg_install.h"
26 #include "opkg_configure.h"
27 #include "opkg_download.h"
28 #include "opkg_remove.h"
29
30 #include "sprintf_alloc.h"
31 #include "file_util.h"
32
33 #include <libbb/libbb.h>
34
35 struct _opkg_t
36 {
37   args_t *args;
38   opkg_conf_t *conf;
39   opkg_option_t *options;
40 };
41
42 /** Private Functions ***/
43
44
45 static int
46 opkg_configure_packages(opkg_conf_t *conf, char *pkg_name)
47 {
48   pkg_vec_t *all;
49   int i;
50   pkg_t *pkg;
51   int r, err = 0;
52
53   all = pkg_vec_alloc ();
54   pkg_hash_fetch_available (&conf->pkg_hash, all);
55
56   for (i = 0; i < all->len; i++)
57   {
58     pkg = all->pkgs[i];
59
60     if (pkg_name && fnmatch (pkg_name, pkg->name, 0))
61       continue;
62
63     if (pkg->state_status == SS_UNPACKED)
64     {
65       r = opkg_configure (conf, pkg);
66       if (r == 0)
67       {
68         pkg->state_status = SS_INSTALLED;
69         pkg->parent->state_status = SS_INSTALLED;
70         pkg->state_flag &= ~SF_PREFER;
71       }
72       else
73       {
74         if (!err)
75           err = r;
76       }
77     }
78   }
79
80   pkg_vec_free (all);
81   return err;
82 }
83
84
85
86 /*** Public API ***/
87
88 opkg_t *
89 opkg_new ()
90 {
91   opkg_t *opkg;
92   opkg = malloc (sizeof (opkg_t));
93
94   opkg->args = malloc (sizeof (args_t));
95   args_init (opkg->args);
96
97   opkg->conf = malloc (sizeof (opkg_conf_t));
98   opkg_conf_init (opkg->conf, opkg->args);
99
100   opkg_init_options_array (opkg->conf, &opkg->options);
101   return opkg;
102 }
103
104 void
105 opkg_free (opkg_t *opkg)
106 {
107   opkg_conf_deinit (opkg->conf);
108   args_deinit (opkg->args);
109 }
110
111 int
112 opkg_read_config_files (opkg_t *opkg)
113 {
114   args_t *a = opkg->args;
115   opkg_conf_t *c = opkg->conf;
116
117   /* Unfortunatly, the easiest way to re-read the config files right now is to
118    * throw away opkg->conf and start again */
119
120   /* copy the settings we need to keep */
121   a->autoremove = c->autoremove;
122   a->force_depends = c->force_depends;
123   a->force_defaults = c->force_defaults;
124   a->force_overwrite = c->force_overwrite;
125   a->force_downgrade = c->force_downgrade;
126   a->force_reinstall = c->force_reinstall;
127   a->force_removal_of_dependent_packages = c->force_removal_of_dependent_packages;
128   a->force_removal_of_essential_packages = c->force_removal_of_essential_packages;
129   a->nodeps = c->nodeps;
130   a->noaction = c->noaction;
131   a->query_all = c->query_all;
132   a->multiple_providers = c->multiple_providers;
133   a->verbosity = c->verbosity;
134
135   if (c->offline_root)
136   {
137     if (a->offline_root) free (a->offline_root);
138     a->offline_root = strdup (c->offline_root);
139   }
140
141   if (c->offline_root_pre_script_cmd)
142   {
143     if (a->offline_root_pre_script_cmd) free (a->offline_root_pre_script_cmd);
144     a->offline_root_pre_script_cmd = strdup (c->offline_root_pre_script_cmd);
145   }
146
147   if (c->offline_root_post_script_cmd)
148   {
149     if (a->offline_root_post_script_cmd) free (a->offline_root_post_script_cmd);
150     a->offline_root_post_script_cmd = strdup (c->offline_root_post_script_cmd);
151   }
152
153   /* throw away old opkg_conf and start again */
154   opkg_conf_deinit (opkg->conf);
155   opkg_conf_init (opkg->conf, opkg->args);
156
157   free (opkg->options);
158   opkg_init_options_array (opkg->conf, &opkg->options);
159
160   return 0;
161 }
162
163 void
164 opkg_get_option (opkg_t *opkg, char *option, void **value)
165 {
166   int i = 0;
167   opkg_option_t *options = opkg->options;
168
169   /* can't store a value in a NULL pointer! */
170   if (!value)
171     return;
172
173   /* look up the option
174    * TODO: this would be much better as a hash table
175    */
176   while (options[i].name)
177   {
178     if (strcmp (options[i].name, option) != 0)
179     {
180       i++;
181       continue;
182     }
183   }
184
185   /* get the option */
186   switch (options[i].type)
187   {
188   case OPKG_OPT_TYPE_BOOL:
189     *((int *) value) = *((int *) options[i].value);
190     return;
191
192   case OPKG_OPT_TYPE_INT:
193     *((int *) value) = *((int *) options[i].value);
194     return;
195
196   case OPKG_OPT_TYPE_STRING:
197     *((char **)value) = strdup (options[i].value);
198     return;
199   }
200
201 }
202
203 void
204 opkg_set_option (opkg_t *opkg, char *option, void *value)
205 {
206   int i = 0;
207   opkg_option_t *options = opkg->options;
208
209   /* NULL values are not defined */
210   if (!value)
211     return;
212
213   /* look up the option
214    * TODO: this would be much better as a hash table
215    */
216   while (options[i].name)
217   {
218     if (strcmp (options[i].name, option) == 0)
219     {
220       break;
221     }
222     i++;
223   }
224
225   /* set the option */
226   switch (options[i].type)
227   {
228   case OPKG_OPT_TYPE_BOOL:
229     if (*((int *) value) == 0)
230       *((int *)options[i].value) = 0;
231     else
232       *((int *)options[i].value) = 1;
233     return;
234
235   case OPKG_OPT_TYPE_INT:
236     *((int *) options[i].value) = *((int *) value);
237     return;
238
239   case OPKG_OPT_TYPE_STRING:
240     *((char **)options[i].value) = strdup (value);
241     return;
242   }
243
244 }
245
246 int
247 opkg_install_package (opkg_t *opkg, const char *package_name, opkg_progress_callback_t progress_callback, void *user_data)
248 {
249   int err;
250   char *package_id = NULL;
251
252   progress_callback (opkg, 0, user_data);
253
254   /* download the package */
255   opkg_prepare_url_for_install (opkg->conf, package_name, &package_id);
256   progress_callback (opkg, 50, user_data);
257
258   /* ... */
259   pkg_info_preinstall_check (opkg->conf);
260
261   if (!package_id)
262     package_id = strdup (package_name);
263
264   /* unpack the package */
265   if (opkg->conf->multiple_providers)
266   {
267     err = opkg_install_multi_by_name (opkg->conf, package_id);
268   }
269   else
270   {
271     err = opkg_install_by_name (opkg->conf, package_id);
272   }
273
274   if (err)
275     return err;
276
277   progress_callback (opkg, 75, user_data);
278
279   /* run configure scripts, etc. */
280   err = opkg_configure_packages (opkg->conf, NULL);
281   if (err)
282     return err;
283
284   /* write out status files and file lists */
285   opkg_conf_write_status_files (opkg->conf);
286   pkg_write_changed_filelists (opkg->conf);
287
288   progress_callback (opkg, 100, user_data);
289   return 0;
290 }
291
292 int
293 opkg_remove_package (opkg_t *opkg, const char *package_name, opkg_progress_callback_t progress_callback, void *user_data)
294 {
295   pkg_t *pkg = NULL;
296   pkg_t *pkg_to_remove;
297
298   if (!opkg)
299     return 1;
300
301   if (!package_name)
302     return 1;
303
304   progress_callback (opkg, 0, user_data);
305
306   pkg_info_preinstall_check (opkg->conf);
307
308   pkg_vec_t *installed_pkgs = pkg_vec_alloc ();
309
310   pkg_hash_fetch_all_installed (&opkg->conf->pkg_hash, installed_pkgs);
311
312   progress_callback (opkg, 25, user_data);
313
314   pkg = pkg_hash_fetch_installed_by_name (&opkg->conf->pkg_hash, package_name);
315
316   if (pkg == NULL)
317   {
318     /* XXX: Error: Package not installed. */
319     return 1;
320   }
321
322   if (pkg->state_status == SS_NOT_INSTALLED)
323   {
324     /* XXX:  Error: Package seems to be not installed (STATUS = NOT_INSTALLED). */
325     return 1;
326   }
327
328   progress_callback (opkg, 75, user_data);
329
330   if (opkg->conf->restrict_to_default_dest)
331   {
332     pkg_to_remove = pkg_hash_fetch_installed_by_name_dest (&opkg->conf->pkg_hash,
333                                                            pkg->name,
334                                                            opkg->conf->default_dest);
335   }
336   else
337   {
338     pkg_to_remove = pkg_hash_fetch_installed_by_name (&opkg->conf->pkg_hash, pkg->name );
339   }
340
341
342   progress_callback (opkg, 75, user_data);
343
344   opkg_remove_pkg (opkg->conf, pkg_to_remove, 0);
345
346   /* write out status files and file lists */
347   opkg_conf_write_status_files (opkg->conf);
348   pkg_write_changed_filelists (opkg->conf);
349
350
351   progress_callback (opkg, 100, user_data);
352   return 0;
353 }
354
355 int
356 opkg_upgrade_package (opkg_t *opkg, const char *package_name, opkg_progress_callback_t progress_callback, void *user_data)
357 {
358   return 1;
359 }
360
361 int
362 opkg_upgrade_all (opkg_t *opkg, opkg_progress_callback_t progress_callback, void *user_data)
363 {
364   return 1;
365 }
366
367 int
368 opkg_update_package_lists (opkg_t *opkg, opkg_progress_callback_t progress_callback, void *user_data)
369 {
370   char *tmp;
371   int err;
372   char *lists_dir;
373   pkg_src_list_elt_t *iter;
374   pkg_src_t *src;
375   int sources_list_count, sources_done;
376
377   progress_callback (opkg, 0, user_data);
378
379   sprintf_alloc (&lists_dir, "%s",
380                  (opkg->conf->restrict_to_default_dest)
381                  ? opkg->conf->default_dest->lists_dir
382                  : opkg->conf->lists_dir);
383
384   if (!file_is_dir (lists_dir))
385   {
386     if (file_exists (lists_dir))
387     {
388       /* XXX: Error: file exists but is not a directory */
389       free (lists_dir);
390       return 1;
391     }
392
393     err = file_mkdir_hier (lists_dir, 0755);
394     if (err)
395     {
396       /* XXX: Error: failed to create directory */
397       free (lists_dir);
398       return 1;
399     }
400   }
401
402   tmp = strdup ("/tmp/opkg.XXXXXX");
403
404   if (mkdtemp (tmp) == NULL)
405   {
406     /* XXX: Error: could not create temporary file name */
407     free (lists_dir);
408     free (tmp);
409     return 1;
410   }
411
412   /* cout the number of sources so we can give some progress updates */
413   sources_list_count = 0;
414   sources_done = 0;
415   iter = opkg->conf->pkg_src_list.head;
416   while (iter)
417   {
418     sources_list_count++;
419     iter = iter->next;
420   }
421
422   for (iter = opkg->conf->pkg_src_list.head; iter; iter = iter->next)
423   {
424     char *url, *list_file_name;
425
426     src = iter->data;
427
428     if (src->extra_data)  /* debian style? */
429       sprintf_alloc (&url, "%s/%s/%s", src->value, src->extra_data,
430                      src->gzip ? "Packages.gz" : "Packages");
431     else
432       sprintf_alloc (&url, "%s/%s", src->value, src->gzip ? "Packages.gz" : "Packages");
433
434     sprintf_alloc (&list_file_name, "%s/%s", lists_dir, src->name);
435     if (src->gzip)
436     {
437       char *tmp_file_name;
438       FILE *in, *out;
439
440       sprintf_alloc (&tmp_file_name, "%s/%s.gz", tmp, src->name);
441
442       /* XXX: Note: downloading url */
443       err = opkg_download (opkg->conf, url, tmp_file_name);
444
445       if (err == 0)
446       {
447         /* XXX: Note: Inflating downloaded file */
448         in = fopen (tmp_file_name, "r");
449         out = fopen (list_file_name, "w");
450         if (in && out)
451           unzip (in, out);
452         else
453           err = 1;
454         if (in)
455           fclose (in);
456         if (out)
457           fclose (out);
458         unlink (tmp_file_name);
459       }
460     }
461     else
462       err = opkg_download (opkg->conf, url, list_file_name);
463
464     if (err)
465     {
466       /* XXX: Error: download error */
467     }
468     free (url);
469
470 #ifdef HAVE_GPGME
471     /* download detached signitures to verify the package lists */
472     /* get the url for the sig file */
473     if (src->extra_data)  /* debian style? */
474       sprintf_alloc (&url, "%s/%s/%s", src->value, src->extra_data,
475                      "Packages.sig");
476     else
477       sprintf_alloc (&url, "%s/%s", src->value, "Packages.sig");
478
479     /* create temporary file for it */
480     char *tmp_file_name;
481
482     sprintf_alloc (&tmp_file_name, "%s/%s", tmp, "Packages.sig");
483
484     err = opkg_download (opkg->conf, url, tmp_file_name);
485     if (err)
486     {
487       /* XXX: Warning: Download failed */
488     }
489     else
490     {
491       int err;
492       err = opkg_verify_file (opkg->conf, list_file_name, tmp_file_name);
493       if (err == 0)
494       {
495         /* XXX: Notice: Signature check passed */
496       }
497       else
498       {
499         /* XXX: Warning: Signature check failed */
500       }
501     }
502     unlink (tmp_file_name);
503     free (tmp_file_name);
504     free (url);
505 #else
506     /* XXX: Note: Signiture check for %s skipped because GPG support was not
507      * enabled in this build
508      */
509 #endif
510     free (list_file_name);
511
512     sources_done++;
513     progress_callback (opkg, 100 * sources_done / sources_list_count, user_data);
514   }
515   rmdir (tmp);
516   free (tmp);
517   free (lists_dir);
518
519   return 0;
520 }