1 /* pkg_alternatives.c - the opkg package management system
3 Copyright (C) 2017 Yousong Zhou
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2, or (at
8 your option) any later version.
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
17 #include <sys/types.h> /* stat */
19 #include <libgen.h> /* dirname */
22 #include "file_util.h"
23 #include "libbb/libbb.h"
24 #include "opkg_message.h"
27 #include "pkg_alternatives.h"
28 #include "sprintf_alloc.h"
30 struct alternative_provider
{
35 static const struct alternative_provider providers
[] = {
38 .altpath
= "/bin/busybox",
42 static const char *pkg_alternatives_check_providers(const char *path
)
49 for (i
= 0; i
< ARRAY_SIZE(providers
); i
++) {
50 pkg
= pkg_hash_fetch_installed_by_name(providers
[i
].name
);
54 files
= pkg_get_installed_files(pkg
);
55 for (iter
= str_list_first(files
); iter
; iter
= str_list_next(files
, iter
)) {
56 if (!strcmp(path
, (char *)(iter
->data
))) {
57 pkg_free_installed_files(pkg
);
58 return providers
[i
].altpath
;
61 pkg_free_installed_files(pkg
);
66 static int pkg_alternatives_update_path(pkg_t
*pkg
, const pkg_vec_t
*installed
, const char *path
)
68 struct pkg_alternatives
*pkg_alts
;
69 struct pkg_alternative
*the_alt
= NULL
;
74 const char *target_path
= NULL
;
76 for (i
= 0; i
< installed
->len
; i
++) {
77 pkg_t
*pkg
= installed
->pkgs
[i
];
78 pkg_alts
= pkg_get_ptr(pkg
, PKG_ALTERNATIVES
);
82 for (j
= 0; j
< pkg_alts
->nalts
; j
++) {
83 struct pkg_alternative
*alt
= pkg_alts
->alts
[j
];
85 if (strcmp(path
, alt
->path
))
87 if (!the_alt
|| the_alt
->prio
< alt
->prio
) {
94 /* path is assumed to be an absolute one */
95 sprintf_alloc(&path_in_dest
, "%s%s", the_pkg
->dest
->root_dir
, &path
[1]);
100 target_path
= the_alt
->altpath
;
102 target_path
= pkg_alternatives_check_providers(path
);
108 r
= lstat(path_in_dest
, &sb
);
112 if (!S_ISLNK(sb
.st_mode
)) {
113 opkg_msg(ERROR
, "%s exists but is not a symlink\n", path_in_dest
);
117 realpath
= xreadlink(path_in_dest
);
118 if (realpath
&& strcmp(realpath
, target_path
))
119 unlink(path_in_dest
);
121 } else if (errno
!= ENOENT
) {
125 char *path_copy
= xstrdup(path_in_dest
);
126 char *path_parent
= dirname(path_copy
);
128 r
= file_mkdir_hier(path_parent
, 0755);
133 r
= symlink(target_path
, path_in_dest
);
134 if (r
&& errno
== EEXIST
) {
136 * the strcmp & unlink check above will make sure that if EEXIST
137 * happens, the symlink target also matches
142 opkg_perror(ERROR
, "failed symlinking %s -> %s", path_in_dest
, target_path
);
146 unlink(path_in_dest
);
155 int pkg_alternatives_update(pkg_t
* pkg
)
159 struct pkg_alternatives
*pkg_alts
;
160 struct pkg_alternative
*alt
= NULL
;
161 pkg_vec_t
*installed
;
163 pkg_alts
= pkg_get_ptr(pkg
, PKG_ALTERNATIVES
);
167 installed
= pkg_vec_alloc();
168 pkg_hash_fetch_all_installed(installed
);
169 for (i
= 0; i
< pkg_alts
->nalts
; i
++) {
170 alt
= pkg_alts
->alts
[i
];
171 r
|= pkg_alternatives_update_path(pkg
, installed
, alt
->path
);
173 pkg_vec_free(installed
);