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 <stdio.h>
19 #include <unistd.h>
20 #include <fnmatch.h>
21 
22 #include "opkg.h"
23 #include "opkg_conf.h"
24 
25 #include "opkg_install.h"
26 #include "opkg_configure.h"
27 #include "opkg_download.h"
28 #include "opkg_remove.h"
29 #include "opkg_upgrade.h"
30 
31 #include "sprintf_alloc.h"
32 #include "file_util.h"
33 
34 #include <libbb/libbb.h>
35 
36 #define opkg_assert(expr) if (!(expr)) { \
37     printf ("opkg: file %s: line %d (%s): Assertation '%s' failed",\
38             __FILE__, __LINE__, __PRETTY_FUNCTION__, # expr); abort (); }
39 
40 #define progress(d, p) d.percentage = p; if (progress_callback) progress_callback (&d, user_data);
41 
42 /** Private Functions ***/
43 
opkg_configure_packages(char * pkg_name)44 static int opkg_configure_packages(char *pkg_name)
45 {
46 	pkg_vec_t *all;
47 	int i;
48 	pkg_t *pkg;
49 	int r, err = 0;
50 
51 	all = pkg_vec_alloc();
52 	pkg_hash_fetch_available(all);
53 
54 	for (i = 0; i < all->len; i++) {
55 		pkg = all->pkgs[i];
56 
57 		if (pkg_name && fnmatch(pkg_name, pkg->name, 0))
58 			continue;
59 
60 		if (pkg->state_status == SS_UNPACKED) {
61 			r = opkg_configure(pkg);
62 			if (r == 0) {
63 				pkg->state_status = SS_INSTALLED;
64 				pkg->parent->state_status = SS_INSTALLED;
65 				pkg->state_flag &= ~SF_PREFER;
66 			} else {
67 				if (!err)
68 					err = r;
69 			}
70 		}
71 	}
72 
73 	pkg_vec_free(all);
74 	return err;
75 }
76 
77 static struct opkg_conf saved_conf;
78 /*** Public API ***/
79 
opkg_new()80 int opkg_new()
81 {
82 	saved_conf = *conf;
83 
84 	if (opkg_conf_init())
85 		goto err0;
86 
87 	if (opkg_conf_load())
88 		goto err0;
89 
90 	if (pkg_hash_load_feeds(0))
91 		goto err1;
92 
93 	if (pkg_hash_load_status_files())
94 		goto err1;
95 
96 	return 0;
97 
98 err1:
99 	pkg_hash_deinit();
100 err0:
101 	opkg_conf_deinit();
102 	return -1;
103 }
104 
opkg_free(void)105 void opkg_free(void)
106 {
107 	opkg_conf_deinit();
108 }
109 
opkg_re_read_config_files(void)110 int opkg_re_read_config_files(void)
111 {
112 	opkg_free();
113 	*conf = saved_conf;
114 	return opkg_new();
115 }
116 
opkg_get_option(char * option,void ** value)117 void opkg_get_option(char *option, void **value)
118 {
119 	int i = 0;
120 	extern opkg_option_t options[];
121 
122 	/* look up the option
123 	 * TODO: this would be much better as a hash table
124 	 */
125 	while (options[i].name) {
126 		if (strcmp(options[i].name, option) != 0) {
127 			i++;
128 			continue;
129 		}
130 	}
131 
132 	/* get the option */
133 	switch (options[i].type) {
134 	case OPKG_OPT_TYPE_BOOL:
135 		*((int *)value) = *((int *)options[i].value);
136 		return;
137 
138 	case OPKG_OPT_TYPE_INT:
139 		*((int *)value) = *((int *)options[i].value);
140 		return;
141 
142 	case OPKG_OPT_TYPE_STRING:
143 		*((char **)value) = xstrdup(options[i].value);
144 		return;
145 	}
146 
147 }
148 
opkg_set_option(char * option,void * value)149 void opkg_set_option(char *option, void *value)
150 {
151 	int i = 0, found = 0;
152 	extern opkg_option_t options[];
153 
154 	opkg_assert(option != NULL);
155 	opkg_assert(value != NULL);
156 
157 	/* look up the option
158 	 * TODO: this would be much better as a hash table
159 	 */
160 	while (options[i].name) {
161 		if (strcmp(options[i].name, option) == 0) {
162 			found = 1;
163 			break;
164 		}
165 		i++;
166 	}
167 
168 	if (!found) {
169 		opkg_msg(ERROR, "Invalid option: %s\n", option);
170 		return;
171 	}
172 
173 	/* set the option */
174 	switch (options[i].type) {
175 	case OPKG_OPT_TYPE_BOOL:
176 		if (*((int *)value) == 0)
177 			*((int *)options[i].value) = 0;
178 		else
179 			*((int *)options[i].value) = 1;
180 		return;
181 
182 	case OPKG_OPT_TYPE_INT:
183 		*((int *)options[i].value) = *((int *)value);
184 		return;
185 
186 	case OPKG_OPT_TYPE_STRING:
187 		*((char **)options[i].value) = xstrdup(value);
188 		return;
189 	}
190 
191 }
192 
193 /**
194  * @brief libopkg API: Install package
195  * @param package_name The name of package in which is going to install
196  * @param progress_callback The callback function that report the status to caller.
197  */
198 int
opkg_install_package(const char * package_name,opkg_progress_callback_t progress_callback,void * user_data)199 opkg_install_package(const char *package_name,
200 		     opkg_progress_callback_t progress_callback,
201 		     void *user_data)
202 {
203 	int err;
204 	char *stripped_filename, *local_filename;
205 	opkg_progress_data_t pdata;
206 	pkg_t *old, *new;
207 	pkg_vec_t *deps, *all;
208 	int i;
209 	char **unresolved = NULL;
210 	const char *filename;
211 
212 	opkg_assert(package_name != NULL);
213 
214 	/* ... */
215 	pkg_info_preinstall_check();
216 
217 	/* check to ensure package is not already installed */
218 	old = pkg_hash_fetch_installed_by_name(package_name);
219 	if (old) {
220 		opkg_msg(ERROR, "Package %s is already installed\n",
221 			 package_name);
222 		return -1;
223 	}
224 
225 	new = pkg_hash_fetch_best_installation_candidate_by_name(package_name);
226 	if (!new) {
227 		opkg_msg(ERROR, "Couldn't find package %s\n", package_name);
228 		return -1;
229 	}
230 
231 	new->state_flag |= SF_USER;
232 
233 	pdata.action = -1;
234 	pdata.pkg = new;
235 
236 	progress(pdata, 0);
237 
238 	/* find dependancies and download them */
239 	deps = pkg_vec_alloc();
240 	/* this function does not return the original package, so we insert it later */
241 	pkg_hash_fetch_unsatisfied_dependencies(new, deps, &unresolved);
242 	if (unresolved) {
243 		char **tmp = unresolved;
244 		opkg_msg(ERROR, "Couldn't satisfy the following dependencies"
245 			 " for %s:\n", package_name);
246 		while (*tmp) {
247 			opkg_msg(ERROR, "\t%s", *tmp);
248 			free(*tmp);
249 			tmp++;
250 		}
251 		free(unresolved);
252 		pkg_vec_free(deps);
253 		opkg_message(ERROR, "\n");
254 		return -1;
255 	}
256 
257 	/* insert the package we are installing so that we download it */
258 	pkg_vec_insert(deps, new);
259 
260 	/* download package and dependencies */
261 	for (i = 0; i < deps->len; i++) {
262 		pkg_t *pkg;
263 		char *url, *urlencoded_path;
264 
265 		pkg = deps->pkgs[i];
266 		if (pkg_get_string(pkg, PKG_LOCAL_FILENAME))
267 			continue;
268 
269 		pdata.pkg = pkg;
270 		pdata.action = OPKG_DOWNLOAD;
271 
272 		if (pkg->src == NULL) {
273 			opkg_msg(ERROR, "Package %s not available from any "
274 				 "configured src\n", package_name);
275 			return -1;
276 		}
277 
278 		filename = pkg_get_string(pkg, PKG_FILENAME);
279 		urlencoded_path = urlencode_path(filename);
280 		sprintf_alloc(&url, "%s/%s", pkg->src->value, urlencoded_path);
281 		free(urlencoded_path);
282 
283 		/* Get the filename part, without any directory */
284 		stripped_filename = strrchr(filename, '/');
285 		if (!stripped_filename)
286 			stripped_filename = (char *)filename;
287 
288 		sprintf_alloc(&local_filename, "%s/%s", conf->tmp_dir,
289 			      stripped_filename);
290 
291 		pkg_set_string(pkg, PKG_LOCAL_FILENAME, local_filename);
292 
293 		err = opkg_download(url, local_filename, 0);
294 		free(url);
295 
296 		if (err) {
297 			pkg_vec_free(deps);
298 			return -1;
299 		}
300 
301 	}
302 	pkg_vec_free(deps);
303 
304 	/* clear depenacy checked marks, left by pkg_hash_fetch_unsatisfied_dependencies */
305 	all = pkg_vec_alloc();
306 	pkg_hash_fetch_available(all);
307 	for (i = 0; i < all->len; i++) {
308 		all->pkgs[i]->parent->dependencies_checked = 0;
309 	}
310 	pkg_vec_free(all);
311 
312 	/* 75% of "install" progress is for downloading */
313 	pdata.pkg = new;
314 	pdata.action = OPKG_INSTALL;
315 	progress(pdata, 75);
316 
317 	/* unpack the package */
318 	err = opkg_install_pkg(new, 0);
319 
320 	if (err) {
321 		return -1;
322 	}
323 
324 	progress(pdata, 75);
325 
326 	/* run configure scripts, etc. */
327 	err = opkg_configure_packages(NULL);
328 	if (err) {
329 		return -1;
330 	}
331 
332 	/* write out status files and file lists */
333 	opkg_conf_write_status_files();
334 	pkg_write_changed_filelists();
335 
336 	progress(pdata, 100);
337 	return 0;
338 }
339 
340 int
opkg_remove_package(const char * package_name,opkg_progress_callback_t progress_callback,void * user_data)341 opkg_remove_package(const char *package_name,
342 		    opkg_progress_callback_t progress_callback, void *user_data)
343 {
344 	int err;
345 	pkg_t *pkg = NULL;
346 	pkg_t *pkg_to_remove;
347 	opkg_progress_data_t pdata;
348 
349 	opkg_assert(package_name != NULL);
350 
351 	pkg_info_preinstall_check();
352 
353 	pkg = pkg_hash_fetch_installed_by_name(package_name);
354 
355 	if (pkg == NULL || pkg->state_status == SS_NOT_INSTALLED) {
356 		opkg_msg(ERROR, "Package %s not installed\n", package_name);
357 		return -1;
358 	}
359 
360 	pdata.action = OPKG_REMOVE;
361 	pdata.pkg = pkg;
362 	progress(pdata, 0);
363 
364 	if (conf->restrict_to_default_dest) {
365 		pkg_to_remove = pkg_hash_fetch_installed_by_name_dest(pkg->name,
366 								      conf->
367 								      default_dest);
368 	} else {
369 		pkg_to_remove = pkg_hash_fetch_installed_by_name(pkg->name);
370 	}
371 
372 	progress(pdata, 75);
373 
374 	err = opkg_remove_pkg(pkg_to_remove, 0);
375 
376 	/* write out status files and file lists */
377 	opkg_conf_write_status_files();
378 	pkg_write_changed_filelists();
379 
380 	progress(pdata, 100);
381 	return (err) ? -1 : 0;
382 }
383 
384 int
opkg_upgrade_package(const char * package_name,opkg_progress_callback_t progress_callback,void * user_data)385 opkg_upgrade_package(const char *package_name,
386 		     opkg_progress_callback_t progress_callback,
387 		     void *user_data)
388 {
389 	int err;
390 	pkg_t *pkg;
391 	opkg_progress_data_t pdata;
392 
393 	opkg_assert(package_name != NULL);
394 
395 	pkg_info_preinstall_check();
396 
397 	if (conf->restrict_to_default_dest) {
398 		pkg = pkg_hash_fetch_installed_by_name_dest(package_name,
399 							    conf->default_dest);
400 	} else {
401 		pkg = pkg_hash_fetch_installed_by_name(package_name);
402 	}
403 
404 	if (!pkg) {
405 		opkg_msg(ERROR, "Package %s not installed\n", package_name);
406 		return -1;
407 	}
408 
409 	pdata.action = OPKG_INSTALL;
410 	pdata.pkg = pkg;
411 	progress(pdata, 0);
412 
413 	err = opkg_upgrade_pkg(pkg);
414 	if (err) {
415 		return -1;
416 	}
417 	progress(pdata, 75);
418 
419 	err = opkg_configure_packages(NULL);
420 	if (err) {
421 		return -1;
422 	}
423 
424 	/* write out status files and file lists */
425 	opkg_conf_write_status_files();
426 	pkg_write_changed_filelists();
427 
428 	progress(pdata, 100);
429 	return 0;
430 }
431 
432 int
opkg_upgrade_all(opkg_progress_callback_t progress_callback,void * user_data)433 opkg_upgrade_all(opkg_progress_callback_t progress_callback, void *user_data)
434 {
435 	pkg_vec_t *installed;
436 	int err = 0;
437 	int i;
438 	pkg_t *pkg;
439 	opkg_progress_data_t pdata;
440 
441 	pdata.action = OPKG_INSTALL;
442 	pdata.pkg = NULL;
443 
444 	progress(pdata, 0);
445 
446 	installed = pkg_vec_alloc();
447 	pkg_info_preinstall_check();
448 
449 	pkg_hash_fetch_all_installed(installed);
450 	for (i = 0; i < installed->len; i++) {
451 		pkg = installed->pkgs[i];
452 
453 		pdata.pkg = pkg;
454 		progress(pdata, 99 * i / installed->len);
455 
456 		err += opkg_upgrade_pkg(pkg);
457 	}
458 	pkg_vec_free(installed);
459 
460 	if (err)
461 		return 1;
462 
463 	err = opkg_configure_packages(NULL);
464 	if (err)
465 		return 1;
466 
467 	/* write out status files and file lists */
468 	opkg_conf_write_status_files();
469 	pkg_write_changed_filelists();
470 
471 	pdata.pkg = NULL;
472 	progress(pdata, 100);
473 	return 0;
474 }
475 
476 int
opkg_update_package_lists(opkg_progress_callback_t progress_callback,void * user_data)477 opkg_update_package_lists(opkg_progress_callback_t progress_callback,
478 			  void *user_data)
479 {
480 	char *tmp;
481 	int err, result = 0;
482 	char *lists_dir;
483 	pkg_src_list_elt_t *iter;
484 	pkg_src_t *src;
485 	int sources_list_count, sources_done;
486 	opkg_progress_data_t pdata;
487 
488 	pdata.action = OPKG_DOWNLOAD;
489 	pdata.pkg = NULL;
490 	progress(pdata, 0);
491 
492 	sprintf_alloc(&lists_dir, "%s", (conf->restrict_to_default_dest)
493 		      ? conf->default_dest->lists_dir : conf->lists_dir);
494 
495 	if (!file_is_dir(lists_dir)) {
496 		if (file_exists(lists_dir)) {
497 			opkg_msg(ERROR, "%s is not a directory\n", lists_dir);
498 			free(lists_dir);
499 			return 1;
500 		}
501 
502 		err = file_mkdir_hier(lists_dir, 0755);
503 		if (err) {
504 			opkg_msg(ERROR, "Couldn't create lists_dir %s\n",
505 				 lists_dir);
506 			free(lists_dir);
507 			return 1;
508 		}
509 	}
510 
511 	sprintf_alloc(&tmp, "%s/update-XXXXXX", conf->tmp_dir);
512 	if (mkdtemp(tmp) == NULL) {
513 		opkg_perror(ERROR, "Coundn't create temporary directory %s",
514 			    tmp);
515 		free(lists_dir);
516 		free(tmp);
517 		return 1;
518 	}
519 
520 	/* count the number of sources so we can give some progress updates */
521 	sources_list_count = 0;
522 	sources_done = 0;
523 	list_for_each_entry(iter, &conf->pkg_src_list.head, node) {
524 		sources_list_count++;
525 	}
526 
527 	list_for_each_entry(iter, &conf->pkg_src_list.head, node) {
528 		char *url, *list_file_name = NULL;
529 
530 		src = (pkg_src_t *) iter->data;
531 
532 		if (src->extra_data)	/* debian style? */
533 			sprintf_alloc(&url, "%s/%s/%s", src->value,
534 				      src->extra_data,
535 				      src->gzip ? "Packages.gz" : "Packages");
536 		else
537 			sprintf_alloc(&url, "%s/%s", src->value,
538 				      src->gzip ? "Packages.gz" : "Packages");
539 
540 		sprintf_alloc(&list_file_name, "%s/%s", lists_dir, src->name);
541 
542 		if (opkg_download(url, list_file_name, 0)) {
543 			opkg_msg(ERROR, "Couldn't retrieve %s\n", url);
544 			result = -1;
545 		}
546 		free(url);
547 
548 #if defined(HAVE_USIGN)
549 		if (conf->check_signature) {
550 			char *sig_file_name;
551 			/* download detached signitures to verify the package lists */
552 			/* get the url for the sig file */
553 			if (src->extra_data)	/* debian style? */
554 				sprintf_alloc(&url, "%s/%s/%s", src->value,
555 					      src->extra_data, "Packages.sig");
556 			else
557 				sprintf_alloc(&url, "%s/%s", src->value,
558 					      "Packages.sig");
559 
560 			/* create filename for signature */
561 			sprintf_alloc(&sig_file_name, "%s/%s.sig", lists_dir,
562 				      src->name);
563 
564 			/* make sure there is no existing signature file */
565 			unlink(sig_file_name);
566 
567 			err = opkg_download(url, sig_file_name, 0);
568 			if (err) {
569 				opkg_msg(ERROR, "Couldn't retrieve %s\n", url);
570 			} else {
571 				int err;
572 				err = opkg_verify_file(list_file_name,
573 						       sig_file_name);
574 				if (err == 0) {
575 					opkg_msg(INFO, "Signature check "
576 						 "passed for %s",
577 						 list_file_name);
578 				} else {
579 					opkg_msg(ERROR, "Signature check "
580 						 "failed for %s",
581 						 list_file_name);
582 				}
583 			}
584 			free(sig_file_name);
585 			free(url);
586 		}
587 #else
588 		opkg_msg(INFO, "Signature check skipped for %s as GPG support"
589 			 " has not been enabled in this build\n",
590 			 list_file_name);
591 #endif
592 		free(list_file_name);
593 
594 		sources_done++;
595 		progress(pdata, 100 * sources_done / sources_list_count);
596 	}
597 
598 	rmdir(tmp);
599 	free(tmp);
600 	free(lists_dir);
601 
602 	/* Now re-read the package lists to update package hash tables. */
603 	opkg_re_read_config_files();
604 
605 	return result;
606 }
607 
pkg_compare_names_and_version(const void * a0,const void * b0)608 static int pkg_compare_names_and_version(const void *a0, const void *b0)
609 {
610 	const pkg_t *a = *(const pkg_t **)a0;
611 	const pkg_t *b = *(const pkg_t **)b0;
612 	int ret;
613 
614 	ret = strcmp(a->name, b->name);
615 
616 	if (ret == 0)
617 		ret = pkg_compare_versions(a, b);
618 
619 	return ret;
620 }
621 
opkg_list_packages(opkg_package_callback_t callback,void * user_data)622 int opkg_list_packages(opkg_package_callback_t callback, void *user_data)
623 {
624 	pkg_vec_t *all;
625 	int i;
626 
627 	opkg_assert(callback);
628 
629 	all = pkg_vec_alloc();
630 	pkg_hash_fetch_available(all);
631 
632 	pkg_vec_sort(all, pkg_compare_names_and_version);
633 
634 	for (i = 0; i < all->len; i++) {
635 		pkg_t *pkg;
636 
637 		pkg = all->pkgs[i];
638 
639 		callback(pkg, user_data);
640 	}
641 
642 	pkg_vec_free(all);
643 
644 	return 0;
645 }
646 
647 int
opkg_list_upgradable_packages(opkg_package_callback_t callback,void * user_data)648 opkg_list_upgradable_packages(opkg_package_callback_t callback, void *user_data)
649 {
650 	struct active_list *head;
651 	struct active_list *node;
652 	pkg_t *old = NULL, *new = NULL;
653 
654 	opkg_assert(callback);
655 
656 	/* ensure all data is valid */
657 	pkg_info_preinstall_check();
658 
659 	head = prepare_upgrade_list();
660 	for (node = active_list_next(head, head); node;
661 	     node = active_list_next(head, node)) {
662 		old = node->pkg;
663 		new =
664 		    pkg_hash_fetch_best_installation_candidate_by_name(old->
665 								       name);
666 		if (new == NULL)
667 			continue;
668 		callback(new, user_data);
669 	}
670 	active_list_head_delete(head);
671 	return 0;
672 }
673 
opkg_find_package(const char * name,const char * ver,const char * arch,const char * repo)674 pkg_t *opkg_find_package(const char *name, const char *ver, const char *arch,
675 			 const char *repo)
676 {
677 	int pkg_found = 0;
678 	pkg_t *pkg = NULL;
679 	pkg_vec_t *all;
680 	int i;
681 #define sstrcmp(x,y) (x && y) ? strcmp (x, y) : 0
682 
683 	all = pkg_vec_alloc();
684 	pkg_hash_fetch_available(all);
685 	for (i = 0; i < all->len; i++) {
686 		char *pkgv;
687 
688 		pkg = all->pkgs[i];
689 
690 		/* check name */
691 		if (sstrcmp(pkg->name, name))
692 			continue;
693 
694 		/* check version */
695 		pkgv = pkg_version_str_alloc(pkg);
696 		if (sstrcmp(pkgv, ver)) {
697 			free(pkgv);
698 			continue;
699 		}
700 		free(pkgv);
701 
702 		/* check architecture */
703 		if (arch) {
704 			if (sstrcmp(pkg_get_architecture(pkg), arch))
705 				continue;
706 		}
707 
708 		/* check repository */
709 		if (repo) {
710 			if (sstrcmp(pkg->src->name, repo))
711 				continue;
712 		}
713 
714 		/* match found */
715 		pkg_found = 1;
716 		break;
717 	}
718 
719 	pkg_vec_free(all);
720 
721 	return pkg_found ? pkg : NULL;
722 }
723 
724 /**
725  * @brief Check the accessibility of repositories.
726  * @return return how many repositories cannot access. 0 means all okay.
727  */
opkg_repository_accessibility_check(void)728 int opkg_repository_accessibility_check(void)
729 {
730 	pkg_src_list_elt_t *iter;
731 	str_list_elt_t *iter1;
732 	str_list_t *src;
733 	int repositories = 0;
734 	int ret = 0;
735 	char *repo_ptr;
736 	char *stmp;
737 	char *host, *end;
738 
739 	src = str_list_alloc();
740 
741 	list_for_each_entry(iter, &conf->pkg_src_list.head, node) {
742 		host = strstr(((pkg_src_t *) iter->data)->value, "://") + 3;
743 		end = index(host, '/');
744 		if (strstr(((pkg_src_t *) iter->data)->value, "://") && end)
745 			stmp = xstrndup(((pkg_src_t *) iter->data)->value,
746 					end -
747 					((pkg_src_t *) iter->data)->value);
748 		else
749 			stmp = xstrdup(((pkg_src_t *) iter->data)->value);
750 
751 		for (iter1 = str_list_first(src); iter1;
752 		     iter1 = str_list_next(src, iter1)) {
753 			if (strstr(iter1->data, stmp))
754 				break;
755 		}
756 		if (iter1)
757 			continue;
758 
759 		sprintf_alloc(&repo_ptr, "%s/index.html", stmp);
760 		free(stmp);
761 
762 		str_list_append(src, repo_ptr);
763 		free(repo_ptr);
764 		repositories++;
765 	}
766 
767 	while (repositories > 0) {
768 		iter1 = str_list_pop(src);
769 		repositories--;
770 
771 		if (opkg_download(iter1->data, "/dev/null", 0))
772 			ret++;
773 		str_list_elt_deinit(iter1);
774 	}
775 
776 	free(src);
777 
778 	return ret;
779 }
780