Line data Source code
1 : /*-
2 : * Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org>
4 : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
5 : * All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer
12 : * in this position and unchanged.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : */
28 :
29 : #include <err.h>
30 : #include <getopt.h>
31 : #include <stdio.h>
32 : #include <stdbool.h>
33 : #include <string.h>
34 : #include <sysexits.h>
35 : #include <unistd.h>
36 :
37 : #include <pkg.h>
38 :
39 : #include <bsd_compat.h>
40 :
41 : #include "pkgcli.h"
42 :
43 : #define AUTOMATIC 1U<<0
44 : #define ORIGIN 1U<<1
45 : #define NAME 1U<<2
46 :
47 : void
48 0 : usage_set(void)
49 : {
50 0 : fprintf(stderr, "Usage: pkg set [-a] [-A [01]] [-o <oldorigin>:<neworigin>] [-n <oldname>:<newname>] [-y] [-Cgix] <pkg-name>\n\n");
51 0 : fprintf(stderr, "For more information see 'pkg help set'. \n");
52 0 : }
53 :
54 : static bool
55 2 : check_change_values(const char *opt, char **oldv, char **newv, char guard)
56 : {
57 : const char *semicolon;
58 :
59 2 : if (opt == NULL)
60 0 : return (false);
61 :
62 2 : semicolon = strrchr(opt, ':');
63 :
64 2 : if (semicolon == NULL)
65 0 : return (false);
66 :
67 2 : *oldv = malloc(semicolon - opt + 1);
68 2 : strlcpy(*oldv, opt, semicolon - opt + 1);
69 2 : *newv = strdup(semicolon + 1);
70 :
71 2 : if (guard != '\0') {
72 : /* Check guard symbol in both new and old values */
73 2 : if (strrchr(*oldv, guard) == NULL ||
74 1 : strrchr(*newv, guard) == NULL) {
75 0 : free(*oldv);
76 0 : free(*newv);
77 0 : *oldv = NULL;
78 0 : *newv = NULL;
79 :
80 0 : return (false);
81 : }
82 : }
83 :
84 2 : return (true);
85 : }
86 :
87 : int
88 4 : exec_set(int argc, char **argv)
89 : {
90 4 : struct pkgdb *db = NULL;
91 4 : struct pkgdb_it *it = NULL;
92 4 : struct pkg *pkg = NULL;
93 : int ch;
94 : int i;
95 4 : match_t match = MATCH_EXACT;
96 4 : int64_t newautomatic = -1;
97 4 : bool automatic = false;
98 4 : bool rc = false;
99 4 : const char *changed = NULL;
100 4 : char *newvalue = NULL;
101 4 : char *oldvalue = NULL;
102 4 : unsigned int loads = PKG_LOAD_BASIC;
103 4 : unsigned int sets = 0;
104 4 : unsigned int field = 0, depfield = 0;
105 : int retcode;
106 :
107 4 : struct option longopts[] = {
108 : { "automatic", required_argument, NULL, 'A' },
109 : { "all", no_argument, NULL, 'a' },
110 : { "case-sensitive", no_argument, NULL, 'C' },
111 : { "glob", no_argument, NULL, 'g' },
112 : { "case-insensitive", no_argument, NULL, 'i' },
113 : { "change-origin", required_argument, NULL, 'o' },
114 : { "change-name", required_argument, NULL, 'n' },
115 : { "regex", no_argument, NULL, 'x' },
116 : { "yes", no_argument, NULL, 'y' },
117 : { NULL, 0, NULL, 0 },
118 : };
119 :
120 16 : while ((ch = getopt_long(argc, argv, "+A:aCgio:xyn:", longopts, NULL)) != -1) {
121 8 : switch (ch) {
122 : case 'A':
123 2 : sets |= AUTOMATIC;
124 2 : newautomatic = optarg[0] - '0';
125 2 : if (newautomatic != 0 && newautomatic != 1)
126 0 : errx(EX_USAGE, "Wrong value for -A. "
127 : "Expecting 0 or 1, got: %s",
128 : optarg);
129 2 : break;
130 : case 'a':
131 0 : match = MATCH_ALL;
132 0 : break;
133 : case 'C':
134 0 : pkgdb_set_case_sensitivity(true);
135 0 : break;
136 : case 'g':
137 0 : match = MATCH_GLOB;
138 0 : break;
139 : case 'i':
140 0 : pkgdb_set_case_sensitivity(false);
141 0 : break;
142 : case 'o':
143 1 : sets |= ORIGIN;
144 1 : loads |= PKG_LOAD_DEPS;
145 1 : match = MATCH_ALL;
146 1 : changed = "origin";
147 1 : if (!check_change_values(optarg, &oldvalue, &newvalue, '/')) {
148 0 : errx(EX_USAGE, "Wrong format for -o. "
149 : "Expecting oldorigin:neworigin, got: %s",
150 : optarg);
151 : }
152 1 : break;
153 : case 'n':
154 1 : sets |= NAME;
155 1 : loads |= PKG_LOAD_DEPS;
156 1 : match = MATCH_ALL;
157 1 : changed = "name";
158 1 : if (!check_change_values(optarg, &oldvalue, &newvalue, '\0')) {
159 0 : errx(EX_USAGE, "Wrong format for -n. "
160 : "Expecting oldname:newname, got: %s",
161 : optarg);
162 : }
163 1 : break;
164 : case 'x':
165 0 : match = MATCH_REGEX;
166 0 : break;
167 : case 'y':
168 4 : yes = true;
169 4 : break;
170 : default:
171 0 : free(oldvalue);
172 :
173 0 : usage_set();
174 0 : return (EX_USAGE);
175 : }
176 : }
177 :
178 4 : argc -= optind;
179 4 : argv += optind;
180 :
181 4 : if ((argc < 1 && match != MATCH_ALL) ||
182 6 : (newautomatic == -1 && newvalue == NULL) ||
183 4 : (sets & (NAME|ORIGIN)) == (NAME|ORIGIN)) {
184 0 : usage_set();
185 0 : return (EX_USAGE);
186 : }
187 :
188 4 : if (sets & NAME) {
189 1 : field = PKG_SET_NAME;
190 1 : depfield = PKG_SET_DEPNAME;
191 : }
192 3 : else if (sets & ORIGIN) {
193 1 : field = PKG_SET_ORIGIN;
194 1 : depfield = PKG_SET_DEPORIGIN;
195 : }
196 :
197 4 : retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
198 : PKGDB_DB_LOCAL);
199 4 : if (retcode == EPKG_ENODB) {
200 0 : if (match == MATCH_ALL)
201 0 : return (EX_OK);
202 0 : if (!quiet)
203 0 : warnx("No packages installed. Nothing to do!");
204 0 : return (EX_OK);
205 4 : } else if (retcode == EPKG_ENOACCESS) {
206 0 : warnx("Insufficient privileges to modify the package database");
207 0 : return (EX_NOPERM);
208 4 : } else if (retcode != EPKG_OK) {
209 0 : warnx("Error accessing package database");
210 0 : return (EX_SOFTWARE);
211 : }
212 :
213 4 : if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
214 0 : free(newvalue);
215 0 : return (EX_IOERR);
216 : }
217 :
218 4 : if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
219 0 : pkgdb_close(db);
220 0 : free(newvalue);
221 0 : warnx("Cannot get an exclusive lock on a database, it is locked by another process");
222 0 : return (EX_TEMPFAIL);
223 : }
224 :
225 4 : if (pkgdb_transaction_begin(db, NULL) != EPKG_OK) {
226 0 : pkgdb_close(db);
227 0 : free(newvalue);
228 0 : warnx("Cannot start transaction for update");
229 0 : return (EX_TEMPFAIL);
230 : }
231 :
232 :
233 4 : if (oldvalue != NULL) {
234 2 : match = MATCH_ALL;
235 2 : if ((it = pkgdb_query(db, oldvalue, MATCH_EXACT)) == NULL) {
236 0 : retcode = EX_IOERR;
237 0 : goto cleanup;
238 : }
239 :
240 2 : if (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) != EPKG_OK) {
241 0 : pkg = NULL;
242 : /* fprintf(stderr, "%s not installed\n", oldorigin);
243 : free(oldorigin);
244 : pkgdb_it_free(it);
245 : pkgdb_close(db);
246 : return (EX_SOFTWARE);*/
247 : }
248 :
249 2 : rc = yes;
250 2 : if (!yes) {
251 0 : if (pkg != NULL)
252 0 : rc = query_yesno(false, "Change %S from %S to %S for %n-%v? ",
253 : changed, oldvalue, newvalue, pkg, pkg);
254 : else
255 0 : rc = query_yesno(false, "Change %S from %S to %S for all dependencies? ",
256 : changed, oldvalue, newvalue);
257 : }
258 2 : if (pkg != NULL && rc) {
259 2 : if (pkgdb_set(db, pkg, field, newvalue) != EPKG_OK) {
260 0 : retcode = EX_IOERR;
261 0 : goto cleanup;
262 : }
263 : }
264 2 : pkgdb_it_free(it);
265 : }
266 4 : i = 0;
267 : do {
268 4 : bool saved_rc = rc;
269 :
270 4 : if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
271 0 : retcode = EX_IOERR;
272 0 : goto cleanup;
273 : }
274 :
275 12 : while (pkgdb_it_next(it, &pkg, loads) == EPKG_OK) {
276 4 : if ((sets & AUTOMATIC) == AUTOMATIC) {
277 2 : pkg_get(pkg, PKG_AUTOMATIC, &automatic);
278 2 : if (automatic == newautomatic)
279 0 : continue;
280 2 : if (!rc) {
281 2 : if (newautomatic)
282 1 : rc = query_yesno(false,
283 : "Mark %n-%v as automatically installed? ",
284 : pkg, pkg);
285 : else
286 1 : rc = query_yesno(false,
287 : "Mark %n-%v as not automatically installed? ",
288 : pkg, pkg);
289 : }
290 2 : if (rc)
291 2 : pkgdb_set(db, pkg, PKG_SET_AUTOMATIC, (int)newautomatic);
292 2 : rc = saved_rc;
293 : }
294 4 : if (sets & (ORIGIN|NAME)) {
295 2 : struct pkg_dep *d = NULL;
296 4 : while (pkg_deps(pkg, &d) == EPKG_OK) {
297 : /*
298 : * Do not query user when he has already
299 : * been queried.
300 : */
301 0 : if (pkgdb_set(db, pkg, depfield, oldvalue, newvalue) != EPKG_OK) {
302 0 : retcode = EX_IOERR;
303 0 : goto cleanup;
304 : }
305 : }
306 : }
307 : }
308 4 : pkgdb_it_free(it);
309 4 : i++;
310 4 : } while (i < argc);
311 :
312 : cleanup:
313 4 : free(oldvalue);
314 4 : free(newvalue);
315 4 : pkg_free(pkg);
316 :
317 4 : if (retcode == 0) {
318 4 : pkgdb_transaction_commit(db, NULL);
319 : }
320 : else {
321 0 : pkgdb_transaction_rollback(db, NULL);
322 : }
323 :
324 4 : pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
325 4 : pkgdb_close(db);
326 :
327 4 : return (retcode);
328 : }
|