pkg.c revision 244553
1234313Sbapt/*-
2234313Sbapt * Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org>
3234313Sbapt * All rights reserved.
4234313Sbapt *
5234313Sbapt * Redistribution and use in source and binary forms, with or without
6234313Sbapt * modification, are permitted provided that the following conditions
7234313Sbapt * are met:
8234313Sbapt * 1. Redistributions of source code must retain the above copyright
9234313Sbapt *    notice, this list of conditions and the following disclaimer.
10234313Sbapt * 2. Redistributions in binary form must reproduce the above copyright
11234313Sbapt *    notice, this list of conditions and the following disclaimer in the
12234313Sbapt *    documentation and/or other materials provided with the distribution.
13234313Sbapt *
14234313Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15234313Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16234313Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17234313Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18234313Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19234313Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20234313Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21234313Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22234313Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23234313Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24234313Sbapt * SUCH DAMAGE.
25234313Sbapt */
26234313Sbapt
27234313Sbapt#include <sys/cdefs.h>
28234313Sbapt__FBSDID("$FreeBSD: head/usr.sbin/pkg/pkg.c 244553 2012-12-21 20:01:13Z matthew $");
29234313Sbapt
30234313Sbapt#include <sys/param.h>
31234313Sbapt#include <sys/elf_common.h>
32234313Sbapt#include <sys/endian.h>
33234322Sbapt#include <sys/wait.h>
34234313Sbapt
35234313Sbapt#include <archive.h>
36234313Sbapt#include <archive_entry.h>
37234313Sbapt#include <ctype.h>
38234313Sbapt#include <err.h>
39234313Sbapt#include <errno.h>
40234313Sbapt#include <fcntl.h>
41234322Sbapt#include <fetch.h>
42234313Sbapt#include <gelf.h>
43234351Sbapt#include <paths.h>
44234313Sbapt#include <stdlib.h>
45234313Sbapt#include <stdio.h>
46234313Sbapt#include <string.h>
47234313Sbapt#include <time.h>
48234313Sbapt#include <unistd.h>
49234313Sbapt
50234313Sbapt#include "elf_tables.h"
51243883Sbapt#include "dns_utils.h"
52234313Sbapt
53234313Sbapt#define _LOCALBASE "/usr/local"
54243883Sbapt#define _PKGS_URL "http://pkg.FreeBSD.org"
55234313Sbapt
56234313Sbaptstatic const char *
57234322Sbaptelf_corres_to_string(struct _elf_corres *m, int e)
58234313Sbapt{
59234315Sbapt	int i;
60234313Sbapt
61234313Sbapt	for (i = 0; m[i].string != NULL; i++)
62234313Sbapt		if (m[i].elf_nb == e)
63234313Sbapt			return (m[i].string);
64234313Sbapt
65234313Sbapt	return ("unknown");
66234313Sbapt}
67234313Sbapt
68234313Sbaptstatic int
69234313Sbaptpkg_get_myabi(char *dest, size_t sz)
70234313Sbapt{
71234313Sbapt	Elf *elf;
72234313Sbapt	Elf_Data *data;
73234313Sbapt	Elf_Note note;
74234313Sbapt	Elf_Scn *scn;
75234313Sbapt	char *src, *osname;
76234313Sbapt	const char *abi;
77234351Sbapt	GElf_Ehdr elfhdr;
78234351Sbapt	GElf_Shdr shdr;
79234313Sbapt	int fd, i, ret;
80234313Sbapt	uint32_t version;
81234313Sbapt
82234313Sbapt	version = 0;
83234351Sbapt	ret = -1;
84234313Sbapt	scn = NULL;
85234313Sbapt	abi = NULL;
86234313Sbapt
87234313Sbapt	if (elf_version(EV_CURRENT) == EV_NONE) {
88234322Sbapt		warnx("ELF library initialization failed: %s",
89234322Sbapt		    elf_errmsg(-1));
90234322Sbapt		return (-1);
91234313Sbapt	}
92234313Sbapt
93234313Sbapt	if ((fd = open("/bin/sh", O_RDONLY)) < 0) {
94234313Sbapt		warn("open()");
95234322Sbapt		return (-1);
96234313Sbapt	}
97234313Sbapt
98234313Sbapt	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
99234313Sbapt		ret = -1;
100234313Sbapt		warnx("elf_begin() failed: %s.", elf_errmsg(-1));
101234313Sbapt		goto cleanup;
102234313Sbapt	}
103234313Sbapt
104234313Sbapt	if (gelf_getehdr(elf, &elfhdr) == NULL) {
105234313Sbapt		ret = -1;
106234313Sbapt		warn("getehdr() failed: %s.", elf_errmsg(-1));
107234313Sbapt		goto cleanup;
108234313Sbapt	}
109234313Sbapt
110234313Sbapt	while ((scn = elf_nextscn(elf, scn)) != NULL) {
111234313Sbapt		if (gelf_getshdr(scn, &shdr) != &shdr) {
112234313Sbapt			ret = -1;
113234313Sbapt			warn("getshdr() failed: %s.", elf_errmsg(-1));
114234313Sbapt			goto cleanup;
115234313Sbapt		}
116234313Sbapt
117234313Sbapt		if (shdr.sh_type == SHT_NOTE)
118234313Sbapt			break;
119234313Sbapt	}
120234313Sbapt
121234313Sbapt	if (scn == NULL) {
122234313Sbapt		ret = -1;
123234351Sbapt		warn("failed to get the note section");
124234313Sbapt		goto cleanup;
125234313Sbapt	}
126234313Sbapt
127234313Sbapt	data = elf_getdata(scn, NULL);
128234313Sbapt	src = data->d_buf;
129234315Sbapt	for (;;) {
130234313Sbapt		memcpy(&note, src, sizeof(Elf_Note));
131234313Sbapt		src += sizeof(Elf_Note);
132234313Sbapt		if (note.n_type == NT_VERSION)
133234313Sbapt			break;
134234313Sbapt		src += note.n_namesz + note.n_descsz;
135234313Sbapt	}
136234313Sbapt	osname = src;
137234313Sbapt	src += note.n_namesz;
138234313Sbapt	if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
139234313Sbapt		version = be32dec(src);
140234313Sbapt	else
141234313Sbapt		version = le32dec(src);
142234313Sbapt
143234313Sbapt	for (i = 0; osname[i] != '\0'; i++)
144234313Sbapt		osname[i] = (char)tolower(osname[i]);
145234313Sbapt
146234313Sbapt	snprintf(dest, sz, "%s:%d:%s:%s",
147234322Sbapt	    osname, version / 100000,
148234322Sbapt	    elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
149234313Sbapt	    elf_corres_to_string(wordsize_corres,
150234322Sbapt	    (int)elfhdr.e_ident[EI_CLASS]));
151234313Sbapt
152234351Sbapt	ret = 0;
153234351Sbapt
154234313Sbapt	switch (elfhdr.e_machine) {
155234351Sbapt	case EM_ARM:
156234351Sbapt		snprintf(dest + strlen(dest), sz - strlen(dest),
157234351Sbapt		    ":%s:%s:%s", elf_corres_to_string(endian_corres,
158234351Sbapt		    (int)elfhdr.e_ident[EI_DATA]),
159234351Sbapt		    (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
160234351Sbapt		    "eabi" : "oabi",
161234351Sbapt		    (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
162234351Sbapt		    "softfp" : "vfp");
163234351Sbapt		break;
164234351Sbapt	case EM_MIPS:
165234351Sbapt		/*
166234351Sbapt		 * this is taken from binutils sources:
167234351Sbapt		 * include/elf/mips.h
168234351Sbapt		 * mapping is figured out from binutils:
169234351Sbapt		 * gas/config/tc-mips.c
170234351Sbapt		 */
171234351Sbapt		switch (elfhdr.e_flags & EF_MIPS_ABI) {
172234351Sbapt		case E_MIPS_ABI_O32:
173234351Sbapt			abi = "o32";
174234313Sbapt			break;
175234351Sbapt		case E_MIPS_ABI_N32:
176234351Sbapt			abi = "n32";
177234313Sbapt			break;
178234351Sbapt		default:
179234351Sbapt			if (elfhdr.e_ident[EI_DATA] ==
180234351Sbapt			    ELFCLASS32)
181234351Sbapt				abi = "o32";
182234351Sbapt			else if (elfhdr.e_ident[EI_DATA] ==
183234351Sbapt			    ELFCLASS64)
184234351Sbapt				abi = "n64";
185234351Sbapt			break;
186234351Sbapt		}
187234351Sbapt		snprintf(dest + strlen(dest), sz - strlen(dest),
188234351Sbapt		    ":%s:%s", elf_corres_to_string(endian_corres,
189234351Sbapt		    (int)elfhdr.e_ident[EI_DATA]), abi);
190234351Sbapt		break;
191234313Sbapt	}
192234313Sbapt
193234313Sbaptcleanup:
194234313Sbapt	if (elf != NULL)
195234313Sbapt		elf_end(elf);
196234313Sbapt
197234313Sbapt	close(fd);
198234313Sbapt	return (ret);
199234313Sbapt}
200234313Sbapt
201234313Sbaptstatic int
202234313Sbaptextract_pkg_static(int fd, char *p, int sz)
203234313Sbapt{
204234313Sbapt	struct archive *a;
205234313Sbapt	struct archive_entry *ae;
206234313Sbapt	char *end;
207234313Sbapt	int ret, r;
208234313Sbapt
209234351Sbapt	ret = -1;
210234313Sbapt	a = archive_read_new();
211234351Sbapt	if (a == NULL) {
212234351Sbapt		warn("archive_read_new");
213234351Sbapt		return (ret);
214234351Sbapt	}
215234313Sbapt	archive_read_support_compression_all(a);
216234313Sbapt	archive_read_support_format_tar(a);
217234313Sbapt
218234351Sbapt	if (lseek(fd, 0, 0) == -1) {
219234351Sbapt		warn("lseek");
220234351Sbapt		goto cleanup;
221234351Sbapt	}
222234313Sbapt
223234313Sbapt	if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
224234351Sbapt		warnx("archive_read_open_fd: %s", archive_error_string(a));
225234313Sbapt		goto cleanup;
226234313Sbapt	}
227234313Sbapt
228234313Sbapt	ae = NULL;
229234313Sbapt	while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
230234313Sbapt		end = strrchr(archive_entry_pathname(ae), '/');
231234313Sbapt		if (end == NULL)
232234313Sbapt			continue;
233234313Sbapt
234234313Sbapt		if (strcmp(end, "/pkg-static") == 0) {
235234313Sbapt			r = archive_read_extract(a, ae,
236234322Sbapt			    ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM |
237234322Sbapt			    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL |
238234322Sbapt			    ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR);
239234351Sbapt			strlcpy(p, archive_entry_pathname(ae), sz);
240234313Sbapt			break;
241234313Sbapt		}
242234313Sbapt	}
243234313Sbapt
244234351Sbapt	if (r == ARCHIVE_OK)
245234351Sbapt		ret = 0;
246234351Sbapt	else
247234313Sbapt		warnx("fail to extract pkg-static");
248234313Sbapt
249234313Sbaptcleanup:
250234313Sbapt	archive_read_finish(a);
251234322Sbapt	return (ret);
252234313Sbapt
253234313Sbapt}
254234313Sbapt
255234313Sbaptstatic int
256234313Sbaptinstall_pkg_static(char *path, char *pkgpath)
257234313Sbapt{
258234313Sbapt	int pstat;
259234313Sbapt	pid_t pid;
260234313Sbapt
261234313Sbapt	switch ((pid = fork())) {
262234351Sbapt	case -1:
263234351Sbapt		return (-1);
264234351Sbapt	case 0:
265234351Sbapt		execl(path, "pkg-static", "add", pkgpath, (char *)NULL);
266234351Sbapt		_exit(1);
267234351Sbapt	default:
268234351Sbapt		break;
269234313Sbapt	}
270234313Sbapt
271234351Sbapt	while (waitpid(pid, &pstat, 0) == -1)
272234313Sbapt		if (errno != EINTR)
273234313Sbapt			return (-1);
274234313Sbapt
275234351Sbapt	if (WEXITSTATUS(pstat))
276234351Sbapt		return (WEXITSTATUS(pstat));
277234351Sbapt	else if (WIFSIGNALED(pstat))
278234351Sbapt		return (128 & (WTERMSIG(pstat)));
279234351Sbapt	return (pstat);
280234313Sbapt}
281234313Sbapt
282234313Sbaptstatic int
283234313Sbaptbootstrap_pkg(void)
284234313Sbapt{
285243883Sbapt	struct url *u;
286234313Sbapt	FILE *remote;
287234870Sbapt	FILE *config;
288234870Sbapt	char *site;
289243883Sbapt	struct dns_srvinfo *mirrors, *current;
290243883Sbapt	/* To store _https._tcp. + hostname + \0 */
291243883Sbapt	char zone[MAXHOSTNAMELEN + 13];
292234313Sbapt	char url[MAXPATHLEN];
293234870Sbapt	char conf[MAXPATHLEN];
294234313Sbapt	char abi[BUFSIZ];
295234313Sbapt	char tmppkg[MAXPATHLEN];
296234313Sbapt	char buf[10240];
297234313Sbapt	char pkgstatic[MAXPATHLEN];
298243883Sbapt	int fd, retry, ret, max_retry;
299234351Sbapt	struct url_stat st;
300234313Sbapt	off_t done, r;
301234351Sbapt	time_t now;
302234351Sbapt	time_t last;
303234313Sbapt
304234313Sbapt	done = 0;
305234351Sbapt	last = 0;
306243883Sbapt	max_retry = 3;
307234351Sbapt	ret = -1;
308234313Sbapt	remote = NULL;
309234870Sbapt	config = NULL;
310243883Sbapt	current = mirrors = NULL;
311234313Sbapt
312234351Sbapt	printf("Bootstrapping pkg please wait\n");
313234313Sbapt
314234313Sbapt	if (pkg_get_myabi(abi, MAXPATHLEN) != 0) {
315234351Sbapt		warnx("failed to determine the system ABI");
316234322Sbapt		return (-1);
317234313Sbapt	}
318234313Sbapt
319234351Sbapt	if (getenv("PACKAGESITE") != NULL)
320234870Sbapt		snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", getenv("PACKAGESITE"));
321234351Sbapt	else
322234313Sbapt		snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz",
323234313Sbapt		    getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL,
324234313Sbapt		    getenv("ABI") ? getenv("ABI") : abi);
325234313Sbapt
326234313Sbapt	snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
327234351Sbapt	    getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
328234313Sbapt
329234313Sbapt	if ((fd = mkstemp(tmppkg)) == -1) {
330234313Sbapt		warn("mkstemp()");
331234322Sbapt		return (-1);
332234313Sbapt	}
333234313Sbapt
334243883Sbapt	retry = max_retry;
335234313Sbapt
336243883Sbapt	u = fetchParseURL(url);
337243883Sbapt	while (remote == NULL) {
338243883Sbapt		if (retry == max_retry) {
339243883Sbapt			if (strcmp(u->scheme, "file") != 0) {
340243883Sbapt				snprintf(zone, sizeof(zone),
341243883Sbapt				    "_%s._tcp.%s", u->scheme, u->host);
342243883Sbapt				printf("%s\n", zone);
343243883Sbapt				mirrors = dns_getsrvinfo(zone);
344243883Sbapt				current = mirrors;
345243883Sbapt			}
346243883Sbapt		}
347243883Sbapt
348243883Sbapt		if (mirrors != NULL)
349243883Sbapt			strlcpy(u->host, current->host, sizeof(u->host));
350243883Sbapt
351243883Sbapt		remote = fetchXGet(u, &st, "");
352243883Sbapt		if (remote == NULL) {
353243883Sbapt			--retry;
354243883Sbapt			if (retry <= 0)
355243883Sbapt				goto fetchfail;
356243883Sbapt			if (mirrors == NULL) {
357243883Sbapt				sleep(1);
358243883Sbapt			} else {
359243883Sbapt				current = current->next;
360243883Sbapt				if (current == NULL)
361243883Sbapt					current = mirrors;
362243883Sbapt			}
363243883Sbapt		}
364243883Sbapt	}
365243883Sbapt
366234351Sbapt	if (remote == NULL)
367234351Sbapt		goto fetchfail;
368234351Sbapt
369234313Sbapt	while (done < st.size) {
370234313Sbapt		if ((r = fread(buf, 1, sizeof(buf), remote)) < 1)
371234313Sbapt			break;
372234313Sbapt
373234313Sbapt		if (write(fd, buf, r) != r) {
374234313Sbapt			warn("write()");
375234313Sbapt			goto cleanup;
376234313Sbapt		}
377234313Sbapt
378234313Sbapt		done += r;
379234313Sbapt		now = time(NULL);
380234351Sbapt		if (now > last || done == st.size)
381234313Sbapt			last = now;
382234313Sbapt	}
383234313Sbapt
384234351Sbapt	if (ferror(remote))
385234351Sbapt		goto fetchfail;
386234313Sbapt
387234313Sbapt	if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
388234313Sbapt		ret = install_pkg_static(pkgstatic, tmppkg);
389234313Sbapt
390234870Sbapt	snprintf(conf, MAXPATHLEN, "%s/etc/pkg.conf",
391234870Sbapt	    getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
392234870Sbapt
393234870Sbapt	if (access(conf, R_OK) == -1) {
394234870Sbapt		site = strrchr(url, '/');
395234870Sbapt		if (site == NULL)
396234870Sbapt			goto cleanup;
397234870Sbapt		site[0] = '\0';
398234870Sbapt		site = strrchr(url, '/');
399234870Sbapt		if (site == NULL)
400234870Sbapt			goto cleanup;
401234870Sbapt		site[0] = '\0';
402234870Sbapt
403234870Sbapt		config = fopen(conf, "w+");
404234870Sbapt		if (config == NULL)
405234870Sbapt			goto cleanup;
406235726Sbapt		fprintf(config, "packagesite: %s\n", url);
407234870Sbapt		fclose(config);
408234870Sbapt	}
409234870Sbapt
410234351Sbapt	goto cleanup;
411234351Sbapt
412234351Sbaptfetchfail:
413234351Sbapt	warnx("Error fetching %s: %s", url, fetchLastErrString);
414234351Sbapt
415234313Sbaptcleanup:
416234870Sbapt	if (remote != NULL)
417234870Sbapt		fclose(remote);
418234313Sbapt	close(fd);
419234313Sbapt	unlink(tmppkg);
420234313Sbapt
421234351Sbapt	return (ret);
422234313Sbapt}
423234313Sbapt
424238461Skanstatic const char confirmation_message[] =
425238461Skan"The package management tool is not yet installed on your system.\n"
426238461Skan"Do you want to fetch and install it now? [y/N]: ";
427238461Skan
428238461Skanstatic int
429238461Skanpkg_query_yes_no(void)
430238461Skan{
431238461Skan	int ret, c;
432238461Skan
433238461Skan	c = getchar();
434238461Skan
435238461Skan	if (c == 'y' || c == 'Y')
436238461Skan		ret = 1;
437238461Skan	else
438238461Skan		ret = 0;
439238461Skan
440238461Skan	while (c != '\n' && c != EOF)
441238461Skan		c = getchar();
442238461Skan
443238461Skan	return (ret);
444238461Skan}
445238461Skan
446234313Sbaptint
447234322Sbaptmain(__unused int argc, char *argv[])
448234313Sbapt{
449234313Sbapt	char pkgpath[MAXPATHLEN];
450234313Sbapt
451234313Sbapt	snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
452234322Sbapt	    getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
453234313Sbapt
454238461Skan	if (access(pkgpath, X_OK) == -1) {
455244553Smatthew		/*
456244553Smatthew		 * To allow 'pkg -n' to be used as a reliable test for whether
457244553Smatthew		 * a system is configured to use pkg, don't bootstrap pkg
458244553Smatthew		 * when that argument is given as argv[1].
459244553Smatthew		 */
460244553Smatthew		if ( argv[1] != NULL && strcmp(argv[1], "-n") == 0) {
461244553Smatthew			printf("%s", "pkg is not installed\n");
462244553Smatthew			exit(EXIT_FAILURE);
463244553Smatthew		}
464244553Smatthew
465238461Skan		/*
466238461Skan		 * Do not ask for confirmation if either of stdin or stdout is
467238461Skan		 * not tty. Check the environment to see if user has answer
468238461Skan		 * tucked in there already.
469238461Skan		 */
470239663Sbapt		if (getenv("ASSUME_ALWAYS_YES") == NULL) {
471238461Skan			printf("%s", confirmation_message);
472239664Sbapt			if (!isatty(fileno(stdin)))
473238461Skan				exit(EXIT_FAILURE);
474239664Sbapt
475239664Sbapt			if (pkg_query_yes_no() == 0)
476239663Sbapt				exit(EXIT_FAILURE);
477238461Skan		}
478234351Sbapt		if (bootstrap_pkg() != 0)
479234351Sbapt			exit(EXIT_FAILURE);
480238461Skan	}
481234313Sbapt
482234313Sbapt	execv(pkgpath, argv);
483234313Sbapt
484234351Sbapt	/* NOT REACHED */
485234322Sbapt	return (EXIT_FAILURE);
486234313Sbapt}
487