CRUX-ARM : Home

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