5891616c4411926b60a308bc86eae86b585a7f00
[project/opkg-lede.git] / libopkg / pkg_alternatives.c
1 /* pkg_alternatives.c - the opkg package management system
2
3 Copyright (C) 2017 Yousong Zhou
4
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.
9
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.
14 */
15
16 #include <stdio.h>
17 #include <sys/types.h> /* stat */
18 #include <sys/stat.h>
19 #include <libgen.h> /* dirname */
20 #include <unistd.h>
21
22 #include "file_util.h"
23 #include "libbb/libbb.h"
24 #include "opkg_message.h"
25 #include "pkg.h"
26 #include "pkg_hash.h"
27 #include "pkg_alternatives.h"
28 #include "sprintf_alloc.h"
29
30 struct alternative_provider {
31 char *name;
32 char *altpath;
33 };
34
35 static const struct alternative_provider const providers[] = {
36 {
37 .name = "busybox",
38 .altpath = "/bin/busybox",
39 },
40 };
41
42 static const char *pkg_alternatives_check_providers(const char *path)
43 {
44 pkg_t *pkg;
45 str_list_t *files;
46 str_list_elt_t *iter;
47 int i;
48
49 for (i = 0; i < ARRAY_SIZE(providers); i++) {
50 pkg = pkg_hash_fetch_installed_by_name(providers[i].name);
51 if (!pkg) {
52 continue;
53 }
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;
59 }
60 }
61 pkg_free_installed_files(pkg);
62 }
63 return NULL;
64 }
65
66 static int pkg_alternatives_update_path(pkg_t *pkg, const pkg_vec_t *installed, const char *path)
67 {
68 struct pkg_alternatives *pkg_alts;
69 struct pkg_alternative *the_alt = NULL;
70 pkg_t *the_pkg = pkg;
71 int i, j;
72 int r;
73 char *path_in_dest;
74 const char *target_path = NULL;
75
76 for (i = 0; i < installed->len; i++) {
77 pkg_t *pkg = installed->pkgs[i];
78 pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
79 if (!pkg_alts)
80 continue;
81
82 for (j = 0; j < pkg_alts->nalts; j++) {
83 struct pkg_alternative *alt = pkg_alts->alts[j];
84
85 if (strcmp(path, alt->path))
86 continue;
87 if (!the_alt || the_alt->prio < alt->prio) {
88 the_pkg = pkg;
89 the_alt = alt;
90 }
91 }
92 }
93
94 /* path is assumed to be an absolute one */
95 sprintf_alloc(&path_in_dest, "%s%s", the_pkg->dest->root_dir, &path[1]);
96 if (!path_in_dest)
97 return -1;
98
99 if (the_alt) {
100 target_path = the_alt->altpath;
101 } else {
102 target_path = pkg_alternatives_check_providers(path);
103 }
104
105 if (target_path) {
106 struct stat sb;
107
108 r = lstat(path_in_dest, &sb);
109 if (!r) {
110 char *realpath;
111
112 if (!S_ISLNK(sb.st_mode)) {
113 opkg_msg(ERROR, "%s exists but is not a symlink\n", path_in_dest);
114 r = -1;
115 goto out;
116 }
117 realpath = xreadlink(path_in_dest);
118 if (realpath && strcmp(realpath, target_path))
119 unlink(path_in_dest);
120 free(realpath);
121 } else if (errno != ENOENT) {
122 goto out;
123 }
124 {
125 char *path_copy = xstrdup(path_in_dest);
126 char *path_parent = dirname(path_copy);
127
128 r = file_mkdir_hier(path_parent, 0755);
129 free(path_copy);
130 if (r) {
131 goto out;
132 }
133 r = symlink(target_path, path_in_dest);
134 if (r && errno == EEXIST) {
135 /*
136 * the strcmp & unlink check above will make sure that if EEXIST
137 * happens, the symlink target also matches
138 */
139 r = 0;
140 }
141 if (r) {
142 opkg_perror(ERROR, "failed symlinking %s -> %s", path_in_dest, target_path);
143 }
144 }
145 } else {
146 unlink(path_in_dest);
147 r = 0;
148 }
149
150 out:
151 free(path_in_dest);
152 return r;
153 }
154
155 int pkg_alternatives_update(pkg_t * pkg)
156 {
157 int r = 0;
158 int i;
159 struct pkg_alternatives *pkg_alts;
160 struct pkg_alternative *alt = NULL;
161 pkg_vec_t *installed;
162
163 pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
164 if (!pkg_alts)
165 return 0;
166
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);
172 }
173 pkg_vec_free(installed);
174
175 return r;
176 }