add opkg option http_timeout
[oweals/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 static int pkg_alternatives_update_path(pkg_t *pkg, const pkg_vec_t *installed, const char *path)
31 {
32         struct pkg_alternatives *pkg_alts;
33         struct pkg_alternative *the_alt = NULL;
34         pkg_t *the_pkg = pkg;
35         int i, j;
36         int r;
37         char *path_in_dest;
38
39         for (i = 0; i < installed->len; i++) {
40                 pkg_t *pkg = installed->pkgs[i];
41                 pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
42                 if (!pkg_alts)
43                         continue;
44
45                 for (j = 0; j < pkg_alts->nalts; j++) {
46                         struct pkg_alternative *alt = pkg_alts->alts[j];
47
48                         if (strcmp(path, alt->path))
49                                 continue;
50                         if (!the_alt || the_alt->prio < alt->prio) {
51                                 the_pkg = pkg;
52                                 the_alt = alt;
53                         }
54                 }
55         }
56
57         /* path is assumed to be an absolute one */
58         sprintf_alloc(&path_in_dest, "%s%s", the_pkg->dest->root_dir, &path[1]);
59         if (!path_in_dest)
60                 return -1;
61
62         if (the_alt) {
63                 struct stat sb;
64
65                 r = lstat(path_in_dest, &sb);
66                 if (!r) {
67                         char *realpath;
68
69                         if (!S_ISLNK(sb.st_mode)) {
70                                 opkg_msg(ERROR, "%s exists but is not a symlink\n", path_in_dest);
71                                 r = -1;
72                                 goto out;
73                         }
74                         realpath = xreadlink(path_in_dest);
75                         if (realpath && strcmp(realpath, the_alt->altpath))
76                                 unlink(path_in_dest);
77                         free(realpath);
78                 } else if (errno != ENOENT) {
79                         goto out;
80                 }
81                 {
82                         char *path_copy = xstrdup(path_in_dest);
83                         char *path_parent = dirname(path_copy);
84
85                         r = file_mkdir_hier(path_parent, 0755);
86                         free(path_copy);
87                         if (r) {
88                                 goto out;
89                         }
90                         r = symlink(the_alt->altpath, path_in_dest);
91                         if (r && errno == EEXIST) {
92                                 /*
93                                  * the strcmp & unlink check above will make sure that if EEXIST
94                                  * happens, the symlink target also matches
95                                  */
96                                 r = 0;
97                         }
98                         if (r) {
99                                 opkg_perror(ERROR, "failed symlinking %s -> %s", path_in_dest, the_alt->altpath);
100                         }
101                 }
102         } else {
103                 unlink(path_in_dest);
104                 r = 0;
105         }
106
107 out:
108         free(path_in_dest);
109         return r;
110 }
111
112 int pkg_alternatives_update(pkg_t * pkg)
113 {
114         int r = 0;
115         int i;
116         struct pkg_alternatives *pkg_alts;
117         struct pkg_alternative *alt = NULL;
118         pkg_vec_t *installed;
119
120         pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
121         if (!pkg_alts)
122                 return 0;
123
124         installed = pkg_vec_alloc();
125         pkg_hash_fetch_all_installed(installed);
126         for (i = 0; i < pkg_alts->nalts; i++) {
127                 alt = pkg_alts->alts[i];
128                 r |= pkg_alternatives_update_path(pkg, installed, alt->path);
129         }
130         pkg_vec_free(installed);
131
132         return r;
133 }