CRUX-ARM : Home

Home :: Documentation :: Download :: Development :: Community :: Ports :: Packages :: Bugs :: Links :: About :: Donors
Fixed link to pkgadd creation in pkgrm-cross and pkginfo-cross
[pkgutils-cross.git] / pkgadd.cc
CommitLineData
9ac667e6
SR
1//
2// pkgutils
3//
4// Copyright (c) 2000-2005 Per Liden
d804a38f 5// Copyright (c) 2006-2013 by CRUX team (http://crux.nu)
361643ea
JB
6//
7// Patches for alternative pkgadd.conf file by Jose V Beneyto <sepen@crux.nu>
8//
9ac667e6
SR
9// This program is free software; you can redistribute it and/or modify
10// it under the terms of the GNU General Public License as published by
11// the Free Software Foundation; either version 2 of the License, or
12// (at your option) any later version.
13//
14// This program is distributed in the hope that it will be useful,
15// but WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17// GNU General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License
20// along with this program; if not, write to the Free Software
21// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22// USA.
23//
24
25#include "pkgadd.h"
26#include <fstream>
27#include <iterator>
28#include <cstdio>
29#include <regex.h>
30#include <unistd.h>
31
32void pkgadd::run(int argc, char** argv)
33{
34 //
35 // Check command line options
36 //
37 string o_root;
361643ea 38 string o_config;
9ac667e6
SR
39 string o_package;
40 bool o_upgrade = false;
41 bool o_force = false;
42
43 for (int i = 1; i < argc; i++) {
44 string option(argv[i]);
45 if (option == "-r" || option == "--root") {
46 assert_argument(argv, argc, i);
47 o_root = argv[i + 1];
48 i++;
361643ea
JB
49 } else if (option == "-c" || option == "--config") {
50 assert_argument(argv, argc, i);
51 o_config = argv[i + 1];
52 i++;
9ac667e6
SR
53 } else if (option == "-u" || option == "--upgrade") {
54 o_upgrade = true;
55 } else if (option == "-f" || option == "--force") {
56 o_force = true;
57 } else if (option[0] == '-' || !o_package.empty()) {
58 throw runtime_error("invalid option " + option);
59 } else {
60 o_package = option;
61 }
62 }
63
64 if (o_package.empty())
65 throw runtime_error("option missing");
66
67 //
68 // Check UID
69 //
70 if (getuid())
71 throw runtime_error("only root can install/upgrade packages");
72
73 //
74 // Install/upgrade package
75 //
76 {
77 db_lock lock(o_root, true);
78 db_open(o_root);
79
80 pair<string, pkginfo_t> package = pkg_open(o_package);
361643ea 81 vector<rule_t> config_rules = read_config(o_config);
9ac667e6
SR
82
83 bool installed = db_find_pkg(package.first);
84 if (installed && !o_upgrade)
85 throw runtime_error("package " + package.first + " already installed (use -u to upgrade)");
86 else if (!installed && o_upgrade)
87 throw runtime_error("package " + package.first + " not previously installed (skip -u to install)");
88
3e5b7ed9 89 set<string> non_install_files = apply_install_rules(package.first, package.second, config_rules);
9ac667e6
SR
90 set<string> conflicting_files = db_find_conflicts(package.first, package.second);
91
92 if (!conflicting_files.empty()) {
93 if (o_force) {
94 set<string> keep_list;
95 if (o_upgrade) // Don't remove files matching the rules in configuration
96 keep_list = make_keep_list(conflicting_files, config_rules);
97 db_rm_files(conflicting_files, keep_list); // Remove unwanted conflicts
98 } else {
99 copy(conflicting_files.begin(), conflicting_files.end(), ostream_iterator<string>(cerr, "\n"));
100 throw runtime_error("listed file(s) already installed (use -f to ignore and overwrite)");
101 }
102 }
103
104 set<string> keep_list;
105
106 if (o_upgrade) {
107 keep_list = make_keep_list(package.second.files, config_rules);
108 db_rm_pkg(package.first, keep_list);
109 }
110
111 db_add_pkg(package.first, package.second);
112 db_commit();
3e5b7ed9 113 pkg_install(o_package, keep_list, non_install_files);
9ac667e6
SR
114 ldconfig();
115 }
116}
117
118void pkgadd::print_help() const
119{
120 cout << "usage: " << utilname << " [options] <file>" << endl
121 << "options:" << endl
361643ea
JB
122 << " -u, --upgrade upgrade package with the same name" << endl
123 << " -f, --force force install, overwrite conflicting files" << endl
124 << " -r, --root <path> specify alternative installation root" << endl
125 << " -c, --config <file> use alternate configuration file" << endl
126 << " -v, --version print version and exit" << endl
127 << " -h, --help print help and exit" << endl;
9ac667e6
SR
128}
129
361643ea 130vector<rule_t> pkgadd::read_config(string file) const
9ac667e6
SR
131{
132 vector<rule_t> rules;
133 unsigned int linecount = 0;
361643ea
JB
134 string filename = root + PKGADD_CONF;
135
136 if (!file.empty()) filename = file;
9ac667e6
SR
137 ifstream in(filename.c_str());
138
139 if (in) {
140 while (!in.eof()) {
141 string line;
142 getline(in, line);
143 linecount++;
144 if (!line.empty() && line[0] != '#') {
145 if (line.length() >= PKGADD_CONF_MAXLINE)
146 throw runtime_error(filename + ":" + itos(linecount) + ": line too long, aborting");
147
148 char event[PKGADD_CONF_MAXLINE];
149 char pattern[PKGADD_CONF_MAXLINE];
150 char action[PKGADD_CONF_MAXLINE];
151 char dummy[PKGADD_CONF_MAXLINE];
152 if (sscanf(line.c_str(), "%s %s %s %s", event, pattern, action, dummy) != 3)
153 throw runtime_error(filename + ":" + itos(linecount) + ": wrong number of arguments, aborting");
154
3e5b7ed9 155 if (!strcmp(event, "UPGRADE") || !strcmp(event, "INSTALL")) {
9ac667e6 156 rule_t rule;
3e5b7ed9 157 rule.event = strcmp(event, "UPGRADE") ? INSTALL : UPGRADE;
9ac667e6
SR
158 rule.pattern = pattern;
159 if (!strcmp(action, "YES")) {
160 rule.action = true;
161 } else if (!strcmp(action, "NO")) {
162 rule.action = false;
163 } else
164 throw runtime_error(filename + ":" + itos(linecount) + ": '" +
165 string(action) + "' unknown action, should be YES or NO, aborting");
166
167 rules.push_back(rule);
168 } else
169 throw runtime_error(filename + ":" + itos(linecount) + ": '" +
170 string(event) + "' unknown event, aborting");
171 }
172 }
173 in.close();
174 }
175
176#ifndef NDEBUG
177 cerr << "Configuration:" << endl;
178 for (vector<rule_t>::const_iterator j = rules.begin(); j != rules.end(); j++) {
179 cerr << "\t" << (*j).pattern << "\t" << (*j).action << endl;
180 }
181 cerr << endl;
182#endif
183
184 return rules;
185}
186
187set<string> pkgadd::make_keep_list(const set<string>& files, const vector<rule_t>& rules) const
188{
189 set<string> keep_list;
1e0dfb6d
TS
190 vector<rule_t> found;
191
192 find_rules(rules, UPGRADE, found);
9ac667e6
SR
193
194 for (set<string>::const_iterator i = files.begin(); i != files.end(); i++) {
1e0dfb6d
TS
195 for (vector<rule_t>::reverse_iterator j = found.rbegin(); j != found.rend(); j++) {
196 if (rule_applies_to_file(*j, *i)) {
197 if (!(*j).action)
198 keep_list.insert(keep_list.end(), *i);
199
200 break;
9ac667e6
SR
201 }
202 }
203 }
204
205#ifndef NDEBUG
206 cerr << "Keep list:" << endl;
207 for (set<string>::const_iterator j = keep_list.begin(); j != keep_list.end(); j++) {
208 cerr << " " << (*j) << endl;
209 }
210 cerr << endl;
211#endif
212
213 return keep_list;
214}
1e0dfb6d 215
3e5b7ed9
TS
216set<string> pkgadd::apply_install_rules(const string& name, pkginfo_t& info, const vector<rule_t>& rules)
217{
218 // TODO: better algo(?)
219 set<string> install_set;
220 set<string> non_install_set;
221 vector<rule_t> found;
222
223 find_rules(rules, INSTALL, found);
224
225 for (set<string>::const_iterator i = info.files.begin(); i != info.files.end(); i++) {
226 bool install_file = true;
227
228 for (vector<rule_t>::reverse_iterator j = found.rbegin(); j != found.rend(); j++) {
229 if (rule_applies_to_file(*j, *i)) {
230 install_file = (*j).action;
231 break;
232 }
233 }
234
235 if (install_file)
236 install_set.insert(install_set.end(), *i);
237 else
238 non_install_set.insert(*i);
239 }
240
241 info.files.clear();
242 info.files = install_set;
243
244#ifndef NDEBUG
245 cerr << "Install set:" << endl;
246 for (set<string>::iterator j = info.files.begin(); j != info.files.end(); j++) {
247 cerr << " " << (*j) << endl;
248 }
249 cerr << endl;
250
251 cerr << "Non-Install set:" << endl;
252 for (set<string>::iterator j = non_install_set.begin(); j != non_install_set.end(); j++) {
253 cerr << " " << (*j) << endl;
254 }
255 cerr << endl;
256#endif
257
258 return non_install_set;
259}
260
1e0dfb6d
TS
261void pkgadd::find_rules(const vector<rule_t>& rules, rule_event_t event, vector<rule_t>& found) const
262{
263 for (vector<rule_t>::const_iterator i = rules.begin(); i != rules.end(); i++)
264 if (i->event == event)
265 found.push_back(*i);
266}
267
268bool pkgadd::rule_applies_to_file(const rule_t& rule, const string& file) const
269{
270 regex_t preg;
271 bool ret;
272
273 if (regcomp(&preg, rule.pattern.c_str(), REG_EXTENDED | REG_NOSUB))
274 throw runtime_error("error compiling regular expression '" + rule.pattern + "', aborting");
275
276 ret = !regexec(&preg, file.c_str(), 0, 0, 0);
277 regfree(&preg);
278
279 return ret;
280}