/ src / lock.c
lock.c
  1  /*-
  2   * Copyright (c) 2012-2014 Matthew Seaman <matthew@FreeBSD.org>
  3   * Copyright (c) 2015-2025 Baptiste Daroussin <bapt@FreeBSD.org>
  4   *
  5   * SPDX-License-Identifier: BSD-2-Clause
  6   */
  7  
  8  #include <err.h>
  9  #include <getopt.h>
 10  #include <stdio.h>
 11  #include <stdlib.h>
 12  #include <unistd.h>
 13  
 14  #include <pkg.h>
 15  
 16  #include "pkgcli.h"
 17  
 18  static int exec_lock_unlock(int, char**, int (*lockfct)(struct pkgdb *, struct pkg *, bool batch));
 19  static int do_lock(struct pkgdb *db, struct pkg *pkg, bool batch);
 20  static int do_unlock(struct pkgdb *db, struct pkg *pkg, bool batch);
 21  
 22  void
 23  usage_lock(void)
 24  {
 25  	fprintf(stderr, "Usage: pkg lock [-lqy] [-a|[-Cgix] <pkg-name>]\n");
 26  	fprintf(stderr, "       pkg lock --has-locked-packages\n");
 27  	fprintf(stderr, "       pkg unlock [-lqy] [-a|[-Cgix] <pkg-name>]\n");
 28  	fprintf(stderr, "For more information see 'pkg help lock'.\n");
 29  }
 30  
 31  static int
 32  do_lock(struct pkgdb *db, struct pkg *pkg, bool batch)
 33  {
 34  	if (pkg_is_locked(pkg)) {
 35  		if (batch)
 36  			return (EPKG_OK);
 37  		if (!quiet)
 38  			pkg_printf("%n-%v: already locked\n",
 39  			       pkg, pkg);
 40  		return (EPKG_FATAL);
 41  	}
 42  
 43  	if (!query_yesno(false, "%n-%v: lock this package? ",
 44  				 pkg, pkg))
 45  		return (EPKG_OK);
 46  
 47  	if (!quiet)
 48  		pkg_printf("Locking %n-%v\n", pkg, pkg);
 49  
 50  	return (pkgdb_set(db, pkg, PKG_SET_LOCKED, (int)true));
 51  }
 52  
 53  
 54  static int
 55  do_unlock(struct pkgdb *db, struct pkg *pkg, bool batch)
 56  {
 57  	if (!pkg_is_locked(pkg)) {
 58  		if (batch)
 59  			return (EPKG_OK);
 60  		if (!quiet)
 61  			pkg_printf("%n-%v: already unlocked\n", pkg, pkg);
 62  		return (EPKG_FATAL);
 63  	}
 64  
 65  	if (!query_yesno(false, "%n-%v: unlock this package? ",
 66  				 pkg, pkg))
 67  		return (EPKG_OK);
 68  
 69  	if (!quiet)
 70  		pkg_printf("Unlocking %n-%v\n", pkg, pkg);
 71  
 72  	return (pkgdb_set(db, pkg, PKG_SET_LOCKED, (int)false));
 73  }
 74  
 75  static int
 76  do_lock_unlock(struct pkgdb *db, int match, const char *pkgname,
 77      int (*lockfct)(struct pkgdb *, struct pkg *, bool))
 78  {
 79  	struct pkgdb_it	*it = NULL;
 80  	struct pkg	*pkg = NULL;
 81  	int		 retcode;
 82  	int		 exitcode = EXIT_SUCCESS;
 83  	bool		 gotone = false;
 84  	vec_t(struct pkg *)pkgs = vec_init();
 85  
 86  	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
 87  		pkgdb_close(db);
 88  		warnx("Cannot get an exclusive lock on database. "
 89  		      "It is locked by another process");
 90  		return (EXIT_FAILURE);
 91  	}
 92  
 93  	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
 94  		exitcode = EXIT_FAILURE;
 95  		goto cleanup;
 96  	}
 97  
 98  	while (pkgdb_it_next(it, &pkg, 0) == EPKG_OK) {
 99  		gotone = true;
100  		vec_push(&pkgs, pkg);
101  		pkg = NULL;
102  	}
103  	vec_foreach(pkgs, i) {
104  		retcode = lockfct(db, pkgs.d[i], match != MATCH_EXACT);
105  		if (retcode != EPKG_OK) {
106  			exitcode = EXIT_FAILURE;
107  			goto cleanup;
108  		}
109  	}
110  
111  	/* No package was found matching that name. */
112          if (gotone == false)
113  	        exitcode = EXIT_FAILURE;
114  
115  cleanup:
116  	vec_free_and_free(&pkgs, pkg_free);
117  	pkgdb_it_free(it);
118  
119  	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
120  
121  	return (exitcode);
122  }
123  
124  int
125  exec_lock(int argc, char **argv)
126  {
127  	return (exec_lock_unlock(argc, argv, do_lock));
128  }
129  
130  int
131  exec_unlock(int argc, char **argv)
132  {
133  	return (exec_lock_unlock(argc, argv, do_unlock));
134  }
135  
136  static int
137  list_locked(struct pkgdb *db, bool has_locked)
138  {
139  	struct pkgdb_it	*it = NULL;
140  	struct pkg	*pkg = NULL;
141  	bool		 gotone = false;
142  
143  	if ((it = pkgdb_query_cond(db, " WHERE locked=1", NULL, MATCH_ALL)) == NULL) {
144  		pkgdb_close(db);
145  		return (EXIT_FAILURE);
146  	}
147  
148          while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
149  		if (!gotone) {
150  			gotone = true;
151  			if (has_locked) {
152  				break;
153  			} else {
154  				if (!quiet) {
155  					printf("Currently locked packages:\n");
156  				}
157  			}
158  		}
159  		pkg_printf("%n-%v\n", pkg, pkg);
160  	}
161  
162  	if (!gotone && !quiet && !has_locked)
163  		printf("No locked packages were found\n");
164  
165  	pkg_free(pkg);
166  	pkgdb_it_free(it);
167  
168  	return (gotone ? EXIT_SUCCESS : EXIT_FAILURE);
169  }
170  
171  static int
172  exec_lock_unlock(int argc, char **argv, int (*lockfct)(struct pkgdb *, struct pkg *, bool))
173  {
174  	struct pkgdb	*db = NULL;
175  	int		 match = MATCH_EXACT;
176  	int		 retcode, i;
177  	int		 exitcode = EXIT_SUCCESS;
178  	int		 ch;
179  	bool		 show_locked = false;
180  	bool		 read_only = false;
181  	bool		 has_locked_packages = false;
182  
183  	struct option longopts[] = {
184  		{ "all",		no_argument,	NULL,	'a' },
185  		{ "case-sensitive",	no_argument,	NULL,	'C' },
186  		{ "glob",		no_argument,	NULL,	'g' },
187  		{ "show-locked",	no_argument,	NULL,	'l' },
188  		{ "quiet",		no_argument,	NULL,	'q' },
189  		{ "regex",		no_argument,	NULL,	'x' },
190  		{ "yes",		no_argument,	NULL,	'y' },
191  		{ "has-locked-packages",no_argument,	NULL,	1 },
192  		{ NULL,		0,			NULL,	0   },
193  	};
194  
195  	while ((ch = getopt_long(argc, argv, "+aCgilqxy", longopts, NULL)) != -1) {
196  		switch (ch) {
197  		case 'a':
198  			match = MATCH_ALL;
199  			break;
200  		case 'C':
201  			pkgdb_set_case_sensitivity(true);
202  			break;
203  		case 'g':
204  			match = MATCH_GLOB;
205  			break;
206  		case 'i':
207  			pkgdb_set_case_sensitivity(false);
208  			break;
209  		case 'l':
210  			show_locked = true;
211  			break;
212  		case 'q':
213  			quiet = true;
214  			break;
215  		case 'x':
216  			match = MATCH_REGEX;
217  			break;
218  		case 'y':
219  			yes = true;
220  			break;
221  		case 1:
222  			show_locked = true;
223  			has_locked_packages = true;
224  			break;
225  		default:
226  			usage_lock();
227  			return (EXIT_FAILURE);
228  		}
229          }
230  	argc -= optind;
231  	argv += optind;
232  
233  	/* Allow 'pkg lock -l' (or 'pkg unlock -l') without any
234  	 * package arguments to just display what packages are
235  	 * currently locked.  In this case, we only need a read_only
236  	 * connection to the DB. */
237  
238  	if (show_locked && match != MATCH_ALL && argc == 0)
239  		read_only = true;
240  
241  	if (!show_locked && match != MATCH_ALL && argc == 0) {
242  		usage_lock();
243  		return (EXIT_FAILURE);
244  	}
245  
246  	if (read_only)
247  		retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
248  	else
249  		retcode = pkgdb_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
250  			       PKGDB_DB_LOCAL);
251  	if (retcode == EPKG_ENODB) {
252  		if (match == MATCH_ALL)
253  			return (EXIT_SUCCESS);
254  		if (!quiet)
255  			warnx("No packages installed.  Nothing to do!");
256  		return (EXIT_SUCCESS);
257  	} else if (retcode == EPKG_ENOACCESS) {
258  		warnx("Insufficient privileges to modify the package database");
259  		return (EXIT_FAILURE);
260  	} else if (retcode != EPKG_OK) {
261  		warnx("Error accessing the package database");
262  		return (EXIT_FAILURE);
263  	}
264  
265  	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
266  	if (retcode != EPKG_OK)
267  		return (EXIT_FAILURE);
268  
269  	if (!read_only) {
270  		if (match == MATCH_ALL) {
271  			exitcode = do_lock_unlock(db, match, NULL, lockfct);
272  		} else {
273  			for (i = 0; i < argc; i++) {
274  				exitcode = do_lock_unlock(db, match, argv[i], lockfct);
275  			}
276  		}
277  	}
278  
279  	if (show_locked)
280  		exitcode = list_locked(db, has_locked_packages);
281  
282  	pkgdb_close(db);
283  
284  	return (exitcode);
285  }