Line data Source code
1 : /*-
2 : * Copyright (c) 2011-2013 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : * Copyright (c) 2011 Will Andrews <will@FreeBSD.org>
5 : * Copyright (c) 2011-2012 Marin Atanasov Nikolov <dnaeon@gmail.com>
6 : * Copyright (c) 2014-2015 Matthew Seaman <matthew@FreeBSD.org>
7 : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
8 : * All rights reserved.
9 : *
10 : * Redistribution and use in source and binary forms, with or without
11 : * modification, are permitted provided that the following conditions
12 : * are met:
13 : * 1. Redistributions of source code must retain the above copyright
14 : * notice, this list of conditions and the following disclaimer
15 : * in this position and unchanged.
16 : * 2. Redistributions in binary form must reproduce the above copyright
17 : * notice, this list of conditions and the following disclaimer in the
18 : * documentation and/or other materials provided with the distribution.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
21 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
24 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 : */
31 :
32 : #ifdef HAVE_CONFIG_H
33 : #include "pkg_config.h"
34 : #endif
35 :
36 : #include <sys/param.h>
37 :
38 : #include <sys/stat.h>
39 : #include <sys/queue.h>
40 : #include <sys/sbuf.h>
41 : #include <sys/types.h>
42 : #include <sys/wait.h>
43 :
44 : #include <assert.h>
45 : #include <ctype.h>
46 : #include <err.h>
47 : #include <errno.h>
48 : #include <getopt.h>
49 : #include <inttypes.h>
50 : #include <stdio.h>
51 : #include <stdlib.h>
52 : #include <string.h>
53 : #include <sysexits.h>
54 : #include <unistd.h>
55 : #ifdef HAVE_LIBJAIL
56 : #include <jail.h>
57 : #include <sys/jail.h>
58 : #endif
59 : #include <signal.h>
60 :
61 : #include <pkg.h>
62 :
63 : #include "pkgcli.h"
64 :
65 : /* Used to define why do we show usage message to a user */
66 : enum pkg_usage_reason {
67 : PKG_USAGE_ERROR,
68 : PKG_USAGE_UNKNOWN_COMMAND,
69 : PKG_USAGE_INVALID_ARGUMENTS,
70 : PKG_USAGE_HELP
71 : };
72 :
73 : static void usage(const char *, const char *, FILE *, enum pkg_usage_reason, ...);
74 : static void usage_help(void);
75 : static int exec_help(int, char **);
76 :
77 : static struct commands {
78 : const char * const name;
79 : const char * const desc;
80 : int (*exec)(int argc, char **argv);
81 : void (* const usage)(void);
82 : } cmd[] = {
83 : { "add", "Compatibility interface to install a package", exec_add, usage_add},
84 : { "alias", "List the command line aliases", exec_alias, usage_alias},
85 : { "annotate", "Add, modify or delete tag-value style annotations on packages", exec_annotate, usage_annotate},
86 : { "audit", "Reports vulnerable packages", exec_audit, usage_audit},
87 : { "autoremove", "Removes orphan packages", exec_autoremove, usage_autoremove},
88 : { "backup", "Backs-up and restores the local package database", exec_backup, usage_backup},
89 : { "check", "Checks for missing dependencies and database consistency", exec_check, usage_check},
90 : { "clean", "Cleans old packages from the cache", exec_clean, usage_clean},
91 : { "config", "Display the value of the configuration options", exec_config, usage_config},
92 : { "convert", "Convert database from/to pkgng", exec_convert, usage_convert},
93 : { "create", "Creates software package distributions", exec_create, usage_create},
94 : { "delete", "Deletes packages from the database and the system", exec_delete, usage_delete},
95 : { "fetch", "Fetches packages from a remote repository", exec_fetch, usage_fetch},
96 : { "help", "Displays help information", exec_help, usage_help},
97 : { "info", "Displays information about installed packages", exec_info, usage_info},
98 : { "install", "Installs packages from remote package repositories and local archives", exec_install, usage_install},
99 : { "lock", "Locks package against modifications or deletion", exec_lock, usage_lock},
100 : { "plugins", "Manages plugins and displays information about plugins", exec_plugins, usage_plugins},
101 : { "query", "Queries information about installed packages", exec_query, usage_query},
102 : { "register", "Registers a package into the local database", exec_register, usage_register},
103 : { "remove", "Deletes packages from the database and the system", exec_delete, usage_delete},
104 : { "repo", "Creates a package repository catalogue", exec_repo, usage_repo},
105 : { "rquery", "Queries information in repository catalogues", exec_rquery, usage_rquery},
106 : { "search", "Performs a search of package repository catalogues", exec_search, usage_search},
107 : { "set", "Modifies information about packages in the local database", exec_set, usage_set},
108 : { "ssh", "Package server (to be used via ssh)", exec_ssh, usage_ssh},
109 : { "shell", "Opens a debug shell", exec_shell, usage_shell},
110 : { "shlib", "Displays which packages link against a specific shared library", exec_shlib, usage_shlib},
111 : { "stats", "Displays package database statistics", exec_stats, usage_stats},
112 : { "unlock", "Unlocks a package, allowing modification or deletion", exec_unlock, usage_lock},
113 : { "update", "Updates package repository catalogues", exec_update, usage_update},
114 : { "updating", "Displays UPDATING information for a package", exec_updating, usage_updating},
115 : { "upgrade", "Performs upgrades of packaged software distributions", exec_upgrade, usage_upgrade},
116 : { "version", "Displays the versions of installed packages", exec_version, usage_version},
117 : { "which", "Displays which package installed a specific file", exec_which, usage_which},
118 : };
119 :
120 : static const unsigned int cmd_len = NELEM(cmd);
121 :
122 : static STAILQ_HEAD(, plugcmd) plugins = STAILQ_HEAD_INITIALIZER(plugins);
123 : struct plugcmd {
124 : const char *name;
125 : const char *desc;
126 : int (*exec)(int argc, char **argv);
127 : STAILQ_ENTRY(plugcmd) next;
128 : };
129 :
130 : typedef int (register_cmd)(int idx, const char **name, const char **desc, int (**exec)(int argc, char **argv));
131 : typedef int (nb_cmd)(void);
132 :
133 : static void
134 0 : show_command_names(void)
135 : {
136 : unsigned i;
137 :
138 0 : for(i = 0; i < cmd_len; i++)
139 0 : printf("%s\n", cmd[i].name);
140 :
141 0 : return;
142 : }
143 :
144 : static void
145 0 : usage(const char *conffile, const char *reposdir, FILE *out, enum pkg_usage_reason reason, ...)
146 : {
147 : struct plugcmd *c;
148 0 : bool plugins_enabled = false;
149 : unsigned int i;
150 : const char *arg;
151 : va_list vp;
152 :
153 0 : if (reason == PKG_USAGE_UNKNOWN_COMMAND) {
154 0 : va_start(vp, reason);
155 0 : arg = va_arg(vp, const char *);
156 0 : va_end(vp);
157 0 : fprintf(out, "pkg: unknown command: %s\n", arg);
158 0 : goto out;
159 : }
160 0 : else if (reason == PKG_USAGE_INVALID_ARGUMENTS) {
161 0 : va_start(vp, reason);
162 0 : arg = va_arg(vp, const char *);
163 0 : va_end(vp);
164 0 : fprintf(out, "pkg: %s\n", arg);
165 : }
166 :
167 : #ifdef HAVE_LIBJAIL
168 : #define JAIL_ARG "-j <jail name or id>|"
169 : #else
170 : #define JAIL_ARG
171 : #endif
172 0 : fprintf(out, "Usage: pkg [-v] [-d] [-l] [-N] ["JAIL_ARG"-c <chroot path>|-r <rootdir>] [-C <configuration file>] [-R <repo config dir>] [-o var=value] [-4|-6] <command> [<args>]\n");
173 0 : if (reason == PKG_USAGE_HELP) {
174 0 : fprintf(out, "Global options supported:\n");
175 0 : fprintf(out, "\t%-15s%s\n", "-d", "Increment debug level");
176 : #ifdef HAVE_LIBJAIL
177 0 : fprintf(out, "\t%-15s%s\n", "-j", "Execute pkg(8) inside a jail(8)");
178 : #endif
179 0 : fprintf(out, "\t%-15s%s\n", "-R", "Execute pkg(8) using relocating installation to <rootdir>");
180 0 : fprintf(out, "\t%-15s%s\n", "-c", "Execute pkg(8) inside a chroot(8)");
181 0 : fprintf(out, "\t%-15s%s\n", "-C", "Use the specified configuration file");
182 0 : fprintf(out, "\t%-15s%s\n", "-R", "Directory to search for individual repository configurations");
183 0 : fprintf(out, "\t%-15s%s\n", "-l", "List available commands and exit");
184 0 : fprintf(out, "\t%-15s%s\n", "-v", "Display pkg(8) version");
185 0 : fprintf(out, "\t%-15s%s\n", "-N", "Test if pkg(8) is activated and avoid auto-activation");
186 0 : fprintf(out, "\t%-15s%s\n", "-o", "Override configuration option from the command line");
187 0 : fprintf(out, "\t%-15s%s\n", "-4", "Only use IPv4");
188 0 : fprintf(out, "\t%-15s%s\n", "-6", "Only use IPv6");
189 0 : fprintf(out, "\nCommands supported:\n");
190 :
191 0 : for (i = 0; i < cmd_len; i++)
192 0 : fprintf(out, "\t%-15s%s\n", cmd[i].name, cmd[i].desc);
193 :
194 0 : if (!pkg_initialized() && pkg_ini(conffile, reposdir, 0) != EPKG_OK)
195 0 : errx(EX_SOFTWARE, "Cannot parse configuration file!");
196 :
197 0 : plugins_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));
198 :
199 0 : if (plugins_enabled) {
200 0 : if (pkg_plugins_init() != EPKG_OK)
201 0 : errx(EX_SOFTWARE, "Plugins cannot be loaded");
202 :
203 0 : fprintf(out, "\nCommands provided by plugins:\n");
204 :
205 0 : STAILQ_FOREACH(c, &plugins, next)
206 0 : fprintf(out, "\t%-15s%s\n", c->name, c->desc);
207 : }
208 0 : fprintf(out, "\nFor more information on the different commands"
209 : " see 'pkg help <command>'.\n");
210 0 : exit(EXIT_SUCCESS);
211 : }
212 :
213 : out:
214 0 : fprintf(out, "\nFor more information on available commands and options see 'pkg help'.\n");
215 0 : exit(EX_USAGE);
216 : }
217 :
218 : static void
219 0 : usage_help(void)
220 : {
221 0 : usage(NULL, NULL, stdout, PKG_USAGE_HELP);
222 0 : }
223 :
224 : static int
225 0 : exec_help(int argc, char **argv)
226 : {
227 : char *manpage;
228 0 : bool plugins_enabled = false;
229 : struct plugcmd *c;
230 : unsigned int i;
231 : const pkg_object *all_aliases;
232 : const pkg_object *alias;
233 0 : pkg_iter it = NULL;
234 :
235 0 : if ((argc != 2) || (strcmp("help", argv[1]) == 0)) {
236 0 : usage_help();
237 0 : return(EX_USAGE);
238 : }
239 :
240 0 : for (i = 0; i < cmd_len; i++) {
241 0 : if (strcmp(cmd[i].name, argv[1]) == 0) {
242 0 : if (asprintf(&manpage, "/usr/bin/man pkg-%s", cmd[i].name) == -1)
243 0 : errx(EX_SOFTWARE, "cannot allocate memory");
244 :
245 0 : system(manpage);
246 0 : free(manpage);
247 :
248 0 : return (0);
249 : }
250 : }
251 :
252 0 : plugins_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));
253 :
254 0 : if (plugins_enabled) {
255 0 : STAILQ_FOREACH(c, &plugins, next) {
256 0 : if (strcmp(c->name, argv[1]) == 0) {
257 0 : if (asprintf(&manpage, "/usr/bin/man pkg-%s", c->name) == -1)
258 0 : errx(EX_SOFTWARE, "cannot allocate memory");
259 :
260 0 : system(manpage);
261 0 : free(manpage);
262 :
263 0 : return (0);
264 : }
265 : }
266 : }
267 :
268 0 : if (strcmp(argv[1], "pkg") == 0) {
269 0 : system("/usr/bin/man 8 pkg");
270 0 : return (0);
271 0 : } else if (strcmp(argv[1], "pkg.conf") == 0) {
272 0 : system("/usr/bin/man 5 pkg.conf");
273 0 : return (0);
274 : }
275 :
276 : /* Try aliases */
277 0 : all_aliases = pkg_config_get("ALIAS");
278 0 : while ((alias = pkg_object_iterate(all_aliases, &it))) {
279 0 : if (strcmp(argv[1], pkg_object_key(alias)) == 0) {
280 0 : printf("`%s` is an alias to `%s`\n", argv[1], pkg_object_string(alias));
281 0 : return (0);
282 : }
283 : }
284 :
285 : /* Command name not found */
286 0 : warnx("'%s' is not a valid command.\n", argv[1]);
287 :
288 0 : fprintf(stderr, "See 'pkg help' for more information on the commands.\n");
289 :
290 0 : return (EX_USAGE);
291 : }
292 :
293 : static void
294 2 : show_plugin_info(void)
295 : {
296 : const pkg_object *conf;
297 2 : struct pkg_plugin *p = NULL;
298 :
299 4 : while (pkg_plugins(&p) == EPKG_OK) {
300 0 : conf = pkg_plugin_conf(p);
301 0 : printf("Configuration for plugin: %s\n",
302 : pkg_plugin_get(p, PKG_PLUGIN_NAME));
303 :
304 0 : printf("%s\n", pkg_object_dump(conf));
305 : }
306 2 : }
307 :
308 : static void
309 2 : show_repository_info(void)
310 : {
311 : const char *mirror, *sig;
312 2 : struct pkg_repo *repo = NULL;
313 :
314 2 : printf("\nRepositories:\n");
315 7 : while (pkg_repos(&repo) == EPKG_OK) {
316 3 : switch (pkg_repo_mirror_type(repo)) {
317 : case SRV:
318 0 : mirror = "SRV";
319 0 : break;
320 : case HTTP:
321 0 : mirror = "HTTP";
322 0 : break;
323 : case NOMIRROR:
324 3 : mirror = "NONE";
325 3 : break;
326 : default:
327 0 : mirror = "-unknown-";
328 0 : break;
329 : }
330 3 : switch (pkg_repo_signature_type(repo)) {
331 : case SIG_PUBKEY:
332 0 : sig = "PUBKEY";
333 0 : break;
334 : case SIG_FINGERPRINT:
335 0 : sig = "FINGERPRINTS";
336 0 : break;
337 : case SIG_NONE:
338 3 : sig = "NONE";
339 3 : break;
340 : default:
341 0 : sig = "-unknown-";
342 0 : break;
343 : }
344 :
345 6 : printf(" %s: { \n %-16s: \"%s\",\n %-16s: %s,\n"
346 : " %-16s: %u",
347 : pkg_repo_name(repo),
348 : "url", pkg_repo_url(repo),
349 3 : "enabled", pkg_repo_enabled(repo) ? "yes" : "no",
350 : "priority", pkg_repo_priority(repo));
351 :
352 3 : if (pkg_repo_mirror_type(repo) != NOMIRROR)
353 0 : printf(",\n %-16s: \"%s\"",
354 : "mirror_type", mirror);
355 3 : if (pkg_repo_signature_type(repo) != SIG_NONE)
356 0 : printf(",\n %-16s: \"%s\"",
357 : "signature_type", sig);
358 3 : if (pkg_repo_fingerprints(repo) != NULL)
359 0 : printf(",\n %-16s: \"%s\"",
360 : "fingerprints", pkg_repo_fingerprints(repo));
361 3 : if (pkg_repo_key(repo) != NULL)
362 0 : printf(",\n %-16s: \"%s\"",
363 : "pubkey", pkg_repo_key(repo));
364 3 : printf("\n }\n");
365 : }
366 2 : }
367 :
368 : static void
369 2 : show_version_info(int version)
370 : {
371 2 : if (version > 1)
372 2 : printf("%-24s: ", "Version");
373 :
374 2 : printf(PKG_PORTVERSION""GITHASH"\n");
375 :
376 2 : if (version == 1)
377 0 : exit(EX_OK);
378 :
379 2 : printf("%s\n", pkg_config_dump());
380 2 : show_plugin_info();
381 2 : show_repository_info();
382 :
383 2 : exit(EX_OK);
384 : /* NOTREACHED */
385 : }
386 :
387 : static void
388 1 : do_activation_test(int argc)
389 : {
390 : int count;
391 :
392 : /* Test to see if pkg(8) has been activated. Exit with an
393 : error code if not. Can be combined with -c and -j to test
394 : if pkg is activated in chroot or jail. If there are no
395 : other arguments, and pkg(8) has been activated, show how
396 : many packages have been installed. */
397 :
398 1 : switch (pkg_status(&count)) {
399 : case PKG_STATUS_UNINSTALLED: /* This case shouldn't ever happen... */
400 0 : errx(EX_UNAVAILABLE, "can't execute " PKG_EXEC_NAME
401 : " or " PKG_STATIC_NAME "\n");
402 : /* NOTREACHED */
403 : case PKG_STATUS_NODB:
404 1 : errx(EX_UNAVAILABLE, "package database non-existent");
405 : /* NOTREACHED */
406 : case PKG_STATUS_NOPACKAGES:
407 0 : errx(EX_UNAVAILABLE, "no packages registered");
408 : /* NOTREACHED */
409 : case PKG_STATUS_ACTIVE:
410 0 : if (argc == 0) {
411 0 : warnx("%d packages installed", count);
412 0 : exit(EX_OK);
413 : }
414 0 : break;
415 : }
416 0 : return;
417 : }
418 :
419 : static void
420 57 : export_arg_option (char *arg)
421 : {
422 : char *eqp;
423 : const char *opt;
424 :
425 57 : if ((eqp = strchr(arg, '=')) != NULL) {
426 57 : *eqp = '\0';
427 :
428 57 : if ((opt = getenv (arg)) != NULL) {
429 0 : warnx("option %s is defined in the environment to '%s' but command line "
430 : "option redefines it", arg, opt);
431 0 : setenv(arg, eqp + 1, 1);
432 : }
433 : else {
434 57 : setenv(arg, eqp + 1, 0);
435 : }
436 :
437 57 : *eqp = '=';
438 : }
439 57 : }
440 :
441 : static void
442 381 : start_process_worker(char *const *save_argv)
443 : {
444 381 : int ret = EX_OK;
445 : int status;
446 : pid_t child_pid;
447 :
448 : /* Fork off a child process to do the actual package work.
449 : * The child may be jailed or chrooted. If a restart is required
450 : * (eg. pkg(8) inself was upgraded) the child can exit with
451 : * 'EX_NEEDRESTART' and the same forking process will be
452 : * replayed. This function returns control in the child
453 : * process only. */
454 :
455 : while (1) {
456 381 : child_pid = fork();
457 :
458 381 : if (child_pid == 0) {
459 : /* Load the new Pkg image */
460 200 : if (ret == EX_NEEDRESTART)
461 0 : execvp(getprogname(), save_argv);
462 400 : return;
463 : } else {
464 181 : if (child_pid == -1)
465 0 : err(EX_OSERR, "Failed to fork worker process");
466 :
467 362 : while (waitpid(child_pid, &status, 0) == -1) {
468 0 : if (errno != EINTR)
469 0 : err(EX_OSERR, "Child process pid=%d", (int)child_pid);
470 : }
471 :
472 181 : ret = WEXITSTATUS(status);
473 :
474 181 : if (WIFEXITED(status) && ret != EX_NEEDRESTART)
475 181 : break;
476 0 : if (WIFSIGNALED(status)) {
477 : /* Process got some terminating signal, hence stop the loop */
478 0 : fprintf(stderr, "Child process pid=%d terminated abnormally: %s\n",
479 : (int)child_pid, strsignal (WTERMSIG(status)));
480 0 : ret = -(WTERMSIG(status));
481 0 : break;
482 : }
483 : }
484 0 : }
485 :
486 181 : exit(ret);
487 : /* NOTREACHED */
488 : }
489 :
490 : static int
491 199 : expand_aliases(int argc, char ***argv)
492 : {
493 199 : pkg_iter it = NULL;
494 : const pkg_object *all_aliases;
495 : const pkg_object *alias;
496 : const char *alias_value;
497 : void *buf;
498 199 : char **oldargv = *argv;
499 : char **newargv;
500 : char *args;
501 : int newargc;
502 : int spaces;
503 : int i;
504 : size_t veclen;
505 : size_t arglen;
506 199 : bool matched = false;
507 :
508 199 : all_aliases = pkg_config_get("ALIAS");
509 :
510 199 : while ((alias = pkg_object_iterate(all_aliases, &it))) {
511 4292 : if (strcmp(oldargv[0], pkg_object_key(alias)) == 0) {
512 2 : matched = true;
513 2 : break;
514 : }
515 : }
516 :
517 199 : if (!matched || (alias_value = pkg_object_string(alias)) == NULL)
518 197 : return (argc); /* Nothing to do */
519 :
520 : /* Estimate how many args alias_value will split into by
521 : * counting the number of whitespace characters in it. This
522 : * will be at minimum one less than the final argc. We'll be
523 : * consuming one of the orginal argv, so that balances
524 : * out. */
525 :
526 2 : spaces = pkg_utils_count_spaces(alias_value);
527 2 : arglen = strlen(alias_value) + 1;
528 2 : veclen = sizeof(char *) * (spaces + argc + 1);
529 2 : buf = malloc(veclen + arglen);
530 2 : if (buf == NULL)
531 0 : err(EX_OSERR, "expanding aliases");
532 :
533 2 : newargv = (char **) buf;
534 2 : args = (char *) (buf + veclen);
535 2 : strlcpy(args, alias_value, arglen);
536 :
537 2 : newargc = 0;
538 8 : while(args != NULL) {
539 4 : newargv[newargc++] = pkg_utils_tokenize(&args);
540 : }
541 2 : for (i = 1; i < argc; i++) {
542 0 : newargv[newargc++] = oldargv[i];
543 : }
544 2 : newargv[newargc] = NULL;
545 :
546 2 : *argv = newargv;
547 2 : return (newargc);
548 : }
549 :
550 : int
551 383 : main(int argc, char **argv)
552 : {
553 : unsigned int i;
554 383 : struct commands *command = NULL;
555 383 : unsigned int ambiguous = 0;
556 383 : const char *chroot_path = NULL;
557 383 : const char *rootdir = NULL;
558 : #ifdef HAVE_LIBJAIL
559 : int jid;
560 : #endif
561 383 : const char *jail_str = NULL;
562 : size_t len;
563 : signed char ch;
564 383 : int64_t debug = 0;
565 383 : int version = 0;
566 383 : int ret = EX_OK;
567 383 : bool plugins_enabled = false;
568 383 : bool plugin_found = false;
569 383 : bool show_commands = false;
570 383 : bool activation_test = false;
571 383 : pkg_init_flags init_flags = 0;
572 : struct plugcmd *c;
573 383 : const char *conffile = NULL;
574 383 : const char *reposdir = NULL;
575 : char **save_argv;
576 : int j;
577 :
578 383 : struct option longopts[] = {
579 : { "debug", no_argument, NULL, 'd' },
580 : #ifdef HAVE_LIBJAIL
581 : { "jail", required_argument, NULL, 'j' },
582 : #endif
583 : { "chroot", required_argument, NULL, 'c' },
584 : { "config", required_argument, NULL, 'C' },
585 : { "repo-conf-dir", required_argument, NULL, 'R' },
586 : { "rootdir", required_argument, NULL, 'r' },
587 : { "list", no_argument, NULL, 'l' },
588 : { "version", no_argument, NULL, 'v' },
589 : { "option", required_argument, NULL, 'o' },
590 : { "only-ipv4", no_argument, NULL, '4' },
591 : { "only-ipv6", no_argument, NULL, '6' },
592 : { NULL, 0, NULL, 0 },
593 : };
594 :
595 : /* Set stdout unbuffered */
596 383 : setvbuf(stdout, NULL, _IONBF, 0);
597 :
598 : /* Ignore SIGPIPE */
599 383 : signal(SIGPIPE, SIG_IGN);
600 :
601 383 : if (argc < 2)
602 0 : usage(NULL, NULL, stderr, PKG_USAGE_INVALID_ARGUMENTS, "not enough arguments");
603 :
604 : /* getopt_long() will permute the arg-list unless
605 : * POSIXLY_CORRECT is set in the environment. This is a
606 : * difference to the original getopt() we were using, and
607 : * screws up our 'pkg {pkg-opts} verb {verb-opts}' command
608 : * line concept. */
609 :
610 383 : if (setenv("POSIXLY_CORRECT", "1", 1) == -1)
611 0 : err(EX_SOFTWARE, "setenv() failed");
612 :
613 383 : save_argv = argv;
614 :
615 : #ifdef HAVE_LIBJAIL
616 : #define JAIL_OPT "j:"
617 : #else
618 : #define JAIL_OPT
619 : #endif
620 859 : while ((ch = getopt_long(argc, argv, "+d"JAIL_OPT"c:C:R:r:lNvo:46", longopts, NULL)) != -1) {
621 93 : switch (ch) {
622 : case 'd':
623 0 : debug++;
624 0 : break;
625 : case 'c':
626 0 : chroot_path = optarg;
627 0 : break;
628 : case 'C':
629 25 : conffile = optarg;
630 25 : break;
631 : case 'R':
632 3 : reposdir = optarg;
633 3 : break;
634 : case 'r':
635 2 : rootdir = optarg;
636 2 : break;
637 : #ifdef HAVE_LIBJAIL
638 : case 'j':
639 0 : jail_str = optarg;
640 0 : break;
641 : #endif
642 : case 'l':
643 0 : show_commands = true;
644 0 : break;
645 : case 'N':
646 2 : activation_test = true;
647 2 : break;
648 : case 'v':
649 4 : version++;
650 4 : break;
651 : case 'o':
652 57 : export_arg_option (optarg);
653 57 : break;
654 : case '4':
655 0 : init_flags = PKG_INIT_FLAG_USE_IPV4;
656 0 : break;
657 : case '6':
658 0 : init_flags = PKG_INIT_FLAG_USE_IPV6;
659 0 : break;
660 : default:
661 0 : break;
662 : }
663 : }
664 383 : argc -= optind;
665 383 : argv += optind;
666 :
667 383 : pkg_set_debug_level(debug);
668 :
669 383 : if (version == 1)
670 0 : show_version_info(version);
671 :
672 383 : if (show_commands && version == 0) {
673 0 : show_command_names();
674 0 : exit(EX_OK);
675 : }
676 :
677 383 : if (argc == 0 && version == 0 && !activation_test)
678 0 : usage(conffile, reposdir, stderr, PKG_USAGE_INVALID_ARGUMENTS, "no commands specified");
679 :
680 383 : umask(022);
681 383 : pkg_event_register(&event_callback, &debug);
682 :
683 : /* reset getopt for the next call */
684 383 : optreset = 1;
685 383 : optind = 1;
686 :
687 383 : if (debug == 0 && version == 0)
688 381 : start_process_worker(save_argv);
689 :
690 : #ifdef HAVE_ARC4RANDOM
691 : /* Ensure that random is stirred after a possible fork */
692 202 : arc4random_stir();
693 : #endif
694 :
695 202 : if ((jail_str != NULL && (chroot_path != NULL || rootdir != NULL)) ||
696 0 : (chroot_path != NULL && (jail_str != NULL || rootdir != NULL)) ||
697 1 : (rootdir != NULL && (jail_str != NULL || chroot_path != NULL))) {
698 0 : usage(conffile, reposdir, stderr, PKG_USAGE_INVALID_ARGUMENTS,
699 : "-j, -c and/or -r cannot be used at the same time!\n");
700 : }
701 :
702 202 : if (chroot_path != NULL)
703 0 : if (chroot(chroot_path) == -1)
704 0 : errx(EX_SOFTWARE, "chroot failed!");
705 :
706 : #ifdef HAVE_LIBJAIL
707 202 : if (jail_str != NULL) {
708 0 : jid = jail_getid(jail_str);
709 0 : if (jid < 0)
710 0 : errx(1, "%s", jail_errmsg);
711 :
712 0 : if (jail_attach(jid) == -1)
713 0 : err(1, "jail_attach(%s)", jail_str);
714 : }
715 :
716 202 : if (jail_str != NULL || chroot_path != NULL)
717 0 : if (chdir("/") == -1)
718 0 : errx(EX_SOFTWARE, "chdir() failed");
719 : #endif
720 :
721 202 : if (rootdir != NULL) {
722 1 : if (chdir(rootdir) == -1)
723 0 : errx(EX_SOFTWARE, "chdir() failed");
724 1 : pkg_set_rootdir(rootdir);
725 : }
726 :
727 202 : if (pkg_ini(conffile, reposdir, init_flags) != EPKG_OK)
728 0 : errx(EX_SOFTWARE, "Cannot parse configuration file!");
729 :
730 202 : if (debug > 0)
731 0 : pkg_set_debug_level(debug);
732 :
733 202 : if (atexit(&pkg_shutdown) != 0)
734 0 : errx(EX_SOFTWARE, "register pkg_shutdown() to run at exit");
735 :
736 202 : if (!pkg_compiled_for_same_os_major())
737 0 : warnx("Warning: Major OS version upgrade detected. Running "
738 : "\"pkg-static install -f pkg\" recommended");
739 :
740 :
741 202 : plugins_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));
742 :
743 202 : if (plugins_enabled) {
744 202 : struct pkg_plugin *p = NULL;
745 :
746 202 : if (pkg_plugins_init() != EPKG_OK)
747 0 : errx(EX_SOFTWARE, "Plugins cannot be loaded");
748 :
749 202 : if (atexit(&pkg_plugins_shutdown) != 0)
750 0 : errx(EX_SOFTWARE,
751 : "register pkg_plugins_shutdown() to run at exit");
752 :
753 : /* load commands plugins */
754 404 : while (pkg_plugins(&p) != EPKG_END) {
755 : int n;
756 :
757 0 : nb_cmd *ncmd = pkg_plugin_func(p, "pkg_register_cmd_count");
758 0 : register_cmd *reg = pkg_plugin_func(p, "pkg_register_cmd");
759 0 : if (reg != NULL && ncmd != NULL) {
760 0 : n = ncmd();
761 0 : for (j = 0; j < n ; j++) {
762 0 : c = malloc(sizeof(struct plugcmd));
763 0 : reg(j, &c->name, &c->desc, &c->exec);
764 0 : STAILQ_INSERT_TAIL(&plugins, c, next);
765 : }
766 : }
767 : }
768 : }
769 :
770 202 : if (version > 1)
771 2 : show_version_info(version);
772 :
773 200 : if (activation_test)
774 1 : do_activation_test(argc);
775 :
776 199 : if (argc >= 1 && strcmp(argv[0], "bootstrap") == 0) {
777 0 : if (argc == 1) {
778 0 : printf("pkg(8) already installed, use -f to force.\n");
779 0 : exit(EXIT_SUCCESS);
780 0 : } else if (argc == 2 && strcmp(argv[1], "-f") == 0) {
781 0 : if (access("/usr/sbin/pkg", R_OK) == 0) {
782 : /* Only 10.0+ supported 'bootstrap -f' */
783 : #if __FreeBSD_version < 1000502
784 : printf("Execute these steps to rebootstrap"
785 : " pkg(8):\n");
786 : printf("# pkg delete -f pkg\n");
787 : printf("# /usr/sbin/pkg -v\n");
788 : exit(EXIT_SUCCESS);
789 : #endif
790 0 : printf("pkg(8) is already installed. Forcing "
791 : "reinstallation through pkg(7).\n");
792 0 : execl("/usr/sbin/pkg", "pkg", "bootstrap",
793 : "-f", NULL);
794 : /* NOTREACHED */
795 : } else
796 0 : errx(EXIT_FAILURE, "pkg(7) bootstrapper not"
797 : " found at /usr/sbin/pkg.");
798 : }
799 : }
800 :
801 199 : save_argv = argv;
802 199 : argc = expand_aliases(argc, &argv);
803 :
804 199 : len = strlen(argv[0]);
805 2897 : for (i = 0; i < cmd_len; i++) {
806 2897 : if (strncmp(argv[0], cmd[i].name, len) == 0) {
807 : /* if we have the exact cmd */
808 199 : if (len == strlen(cmd[i].name)) {
809 199 : command = &cmd[i];
810 199 : ambiguous = 0;
811 199 : break;
812 : }
813 :
814 : /*
815 : * we already found a partial match so `argv[0]' is
816 : * an ambiguous shortcut
817 : */
818 0 : ambiguous++;
819 :
820 0 : command = &cmd[i];
821 : }
822 : }
823 :
824 199 : set_globals();
825 :
826 199 : if (command == NULL) {
827 : /* Check if a plugin provides the requested command */
828 0 : ret = EPKG_FATAL;
829 0 : if (plugins_enabled) {
830 0 : STAILQ_FOREACH(c, &plugins, next) {
831 0 : if (strcmp(c->name, argv[0]) == 0) {
832 0 : plugin_found = true;
833 0 : ret = c->exec(argc, argv);
834 0 : break;
835 : }
836 : }
837 : }
838 :
839 0 : if (!plugin_found)
840 0 : usage(conffile, reposdir, stderr, PKG_USAGE_UNKNOWN_COMMAND, argv[0]);
841 :
842 0 : return (ret);
843 : }
844 :
845 199 : if (ambiguous <= 1) {
846 199 : assert(command->exec != NULL);
847 199 : ret = command->exec(argc, argv);
848 : } else {
849 0 : usage(conffile, reposdir, stderr, PKG_USAGE_UNKNOWN_COMMAND, argv[0]);
850 : }
851 :
852 177 : if (save_argv != argv)
853 2 : free(argv);
854 :
855 177 : if (ret == EX_OK && newpkgversion)
856 0 : return (EX_NEEDRESTART);
857 :
858 177 : return (ret);
859 : }
860 :
|