CRUX-ARM : Home

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