1 /*-
2 * Copyright (c) 2012-2014 Matthew Seaman <matthew@FreeBSD.org>
3 * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer
11 * in this position and unchanged.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <err.h>
29 #include <getopt.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33
34 #include <pkg.h>
35
36 #include "pkgcli.h"
37
38 enum action {
39 LOCK,
40 UNLOCK,
41 };
42
43 static int exec_lock_unlock(int, char**, enum action);
44 static int do_lock(struct pkgdb *db, struct pkg *pkg);
45 static int do_unlock(struct pkgdb *db, struct pkg *pkg);
46
47 void
usage_lock(void)48 usage_lock(void)
49 {
50 fprintf(stderr, "Usage: pkg lock [-lqy] [-a|[-Cgix] <pkg-name>]\n");
51 fprintf(stderr, " pkg lock --has-locked-packages\n");
52 fprintf(stderr, " pkg unlock [-lqy] [-a|[-Cgix] <pkg-name>]\n");
53 fprintf(stderr, "For more information see 'pkg help lock'.\n");
54 }
55
56 static int
do_lock(struct pkgdb * db,struct pkg * pkg)57 do_lock(struct pkgdb *db, struct pkg *pkg)
58 {
59 if (pkg_is_locked(pkg)) {
60 if (!quiet)
61 pkg_printf("%n-%v: already locked\n",
62 pkg, pkg);
63 return (EPKG_OK);
64 }
65
66 if (!query_yesno(false, "%n-%v: lock this package? ",
67 pkg, pkg))
68 return (EPKG_OK);
69
70 if (!quiet)
71 pkg_printf("Locking %n-%v\n", pkg, pkg);
72
73 return (pkgdb_set(db, pkg, PKG_SET_LOCKED, (int)true));
74 }
75
76
77 static int
do_unlock(struct pkgdb * db,struct pkg * pkg)78 do_unlock(struct pkgdb *db, struct pkg *pkg)
79 {
80 if (!pkg_is_locked(pkg)) {
81 if (!quiet)
82 pkg_printf("%n-%v: already unlocked\n", pkg, pkg);
83 return (EPKG_OK);
84 }
85
86 if (!query_yesno(false, "%n-%v: unlock this package? ",
87 pkg, pkg))
88 return (EPKG_OK);
89
90 if (!quiet)
91 pkg_printf("Unlocking %n-%v\n", pkg, pkg);
92
93 return (pkgdb_set(db, pkg, PKG_SET_LOCKED, (int)false));
94 }
95
96 static int
do_lock_unlock(struct pkgdb * db,int match,const char * pkgname,enum action action)97 do_lock_unlock(struct pkgdb *db, int match, const char *pkgname,
98 enum action action)
99 {
100 struct pkgdb_it *it = NULL;
101 struct pkg *pkg = NULL;
102 int retcode;
103 int exitcode = EXIT_SUCCESS;
104
105 if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
106 pkgdb_close(db);
107 warnx("Cannot get an exclusive lock on database. "
108 "It is locked by another process");
109 return (EXIT_FAILURE);
110 }
111
112 if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
113 exitcode = EXIT_FAILURE;
114 goto cleanup;
115 }
116
117 while (pkgdb_it_next(it, &pkg, 0) == EPKG_OK) {
118 if (action == LOCK)
119 retcode = do_lock(db, pkg);
120 else
121 retcode = do_unlock(db, pkg);
122
123 if (retcode != EPKG_OK) {
124 exitcode = EXIT_FAILURE;
125 goto cleanup;
126 }
127 }
128
129 cleanup:
130 pkg_free(pkg);
131 pkgdb_it_free(it);
132
133 pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
134
135 return (exitcode);
136 }
137
138 int
exec_lock(int argc,char ** argv)139 exec_lock(int argc, char **argv)
140 {
141 return (exec_lock_unlock(argc, argv, LOCK));
142 }
143
144 int
exec_unlock(int argc,char ** argv)145 exec_unlock(int argc, char **argv)
146 {
147 return (exec_lock_unlock(argc, argv, UNLOCK));
148 }
149
150 static int
list_locked(struct pkgdb * db,bool has_locked)151 list_locked(struct pkgdb *db, bool has_locked)
152 {
153 struct pkgdb_it *it = NULL;
154 struct pkg *pkg = NULL;
155 bool gotone = false;
156
157 if ((it = pkgdb_query_cond(db, " WHERE locked=1", NULL, MATCH_ALL)) == NULL) {
158 pkgdb_close(db);
159 return (EXIT_FAILURE);
160 }
161
162 if (!quiet && !has_locked)
163 printf("Currently locked packages:\n");
164
165 while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
166 gotone = true;
167 if (has_locked)
168 break;
169 pkg_printf("%n-%v\n", pkg, pkg);
170 }
171
172 pkg_free(pkg);
173 pkgdb_it_free(it);
174
175 if (has_locked)
176 return (gotone ? EXIT_SUCCESS : EXIT_FAILURE);
177
178 return (EXIT_SUCCESS);
179 }
180
181 static int
exec_lock_unlock(int argc,char ** argv,enum action action)182 exec_lock_unlock(int argc, char **argv, enum action action)
183 {
184 struct pkgdb *db = NULL;
185 int match = MATCH_EXACT;
186 int retcode, i;
187 int exitcode = EXIT_SUCCESS;
188 int ch;
189 bool show_locked = false;
190 bool read_only = false;
191 bool has_locked_packages = false;
192
193 struct option longopts[] = {
194 { "all", no_argument, NULL, 'a' },
195 { "case-sensitive", no_argument, NULL, 'C' },
196 { "glob", no_argument, NULL, 'g' },
197 { "show-locked", no_argument, NULL, 'l' },
198 { "quiet", no_argument, NULL, 'q' },
199 { "regex", no_argument, NULL, 'x' },
200 { "yes", no_argument, NULL, 'y' },
201 { "has-locked-packages",no_argument, NULL, 1 },
202 { NULL, 0, NULL, 0 },
203 };
204
205 while ((ch = getopt_long(argc, argv, "+aCgilqxy", longopts, NULL)) != -1) {
206 switch (ch) {
207 case 'a':
208 match = MATCH_ALL;
209 break;
210 case 'C':
211 pkgdb_set_case_sensitivity(true);
212 break;
213 case 'g':
214 match = MATCH_GLOB;
215 break;
216 case 'i':
217 pkgdb_set_case_sensitivity(false);
218 break;
219 case 'l':
220 show_locked = true;
221 break;
222 case 'q':
223 quiet = true;
224 break;
225 case 'x':
226 match = MATCH_REGEX;
227 break;
228 case 'y':
229 yes = true;
230 break;
231 case 1:
232 show_locked = true;
233 has_locked_packages = true;
234 break;
235 default:
236 usage_lock();
237 return (EXIT_FAILURE);
238 }
239 }
240 argc -= optind;
241 argv += optind;
242
243 /* Allow 'pkg lock -l' (or 'pkg unlock -l') without any
244 * package arguments to just display what packages are
245 * currently locked. In this case, we only need a read_only
246 * connection to the DB. */
247
248 if (show_locked && match != MATCH_ALL && argc == 0)
249 read_only = true;
250
251 if (!show_locked && match != MATCH_ALL && argc == 0) {
252 usage_lock();
253 return (EXIT_FAILURE);
254 }
255
256 if (read_only)
257 retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
258 else
259 retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
260 PKGDB_DB_LOCAL);
261 if (retcode == EPKG_ENODB) {
262 if (match == MATCH_ALL)
263 return (EXIT_SUCCESS);
264 if (!quiet)
265 warnx("No packages installed. Nothing to do!");
266 return (EXIT_SUCCESS);
267 } else if (retcode == EPKG_ENOACCESS) {
268 warnx("Insufficient privileges to modify the package database");
269 return (EXIT_FAILURE);
270 } else if (retcode != EPKG_OK) {
271 warnx("Error accessing the package database");
272 return (EXIT_FAILURE);
273 }
274
275 retcode = pkgdb_open(&db, PKGDB_DEFAULT);
276 if (retcode != EPKG_OK)
277 return (EXIT_FAILURE);
278
279 if (!read_only) {
280 if (match == MATCH_ALL) {
281 exitcode = do_lock_unlock(db, match, NULL, action);
282 } else {
283 for (i = 0; i < argc; i++) {
284 retcode = do_lock_unlock(db, match, argv[i], action);
285 if (retcode != EXIT_SUCCESS)
286 exitcode = retcode;
287 }
288 }
289 }
290
291 if (show_locked)
292 exitcode = list_locked(db, has_locked_packages);
293
294 pkgdb_close(db);
295
296 return (exitcode == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE);
297 }
298