pkg.c revision 234351
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 234351 2012-04-16 20:41:25Z bapt $");
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"
51234313Sbapt
52234313Sbapt#define _LOCALBASE "/usr/local"
53234313Sbapt#define _PKGS_URL "http://pkgbeta.FreeBSD.org"
54234313Sbapt
55234313Sbaptstatic const char *
56234322Sbaptelf_corres_to_string(struct _elf_corres *m, int e)
57234313Sbapt{
58234315Sbapt	int i;
59234313Sbapt
60234313Sbapt	for (i = 0; m[i].string != NULL; i++)
61234313Sbapt		if (m[i].elf_nb == e)
62234313Sbapt			return (m[i].string);
63234313Sbapt
64234313Sbapt	return ("unknown");
65234313Sbapt}
66234313Sbapt
67234313Sbaptstatic int
68234313Sbaptpkg_get_myabi(char *dest, size_t sz)
69234313Sbapt{
70234313Sbapt	Elf *elf;
71234313Sbapt	Elf_Data *data;
72234313Sbapt	Elf_Note note;
73234313Sbapt	Elf_Scn *scn;
74234313Sbapt	char *src, *osname;
75234313Sbapt	const char *abi;
76234351Sbapt	GElf_Ehdr elfhdr;
77234351Sbapt	GElf_Shdr shdr;
78234313Sbapt	int fd, i, ret;
79234313Sbapt	uint32_t version;
80234313Sbapt
81234313Sbapt	version = 0;
82234351Sbapt	ret = -1;
83234313Sbapt	scn = NULL;
84234313Sbapt	abi = NULL;
85234313Sbapt
86234313Sbapt	if (elf_version(EV_CURRENT) == EV_NONE) {
87234322Sbapt		warnx("ELF library initialization failed: %s",
88234322Sbapt		    elf_errmsg(-1));
89234322Sbapt		return (-1);
90234313Sbapt	}
91234313Sbapt
92234313Sbapt	if ((fd = open("/bin/sh", O_RDONLY)) < 0) {
93234313Sbapt		warn("open()");
94234322Sbapt		return (-1);
95234313Sbapt	}
96234313Sbapt
97234313Sbapt	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
98234313Sbapt		ret = -1;
99234313Sbapt		warnx("elf_begin() failed: %s.", elf_errmsg(-1));
100234313Sbapt		goto cleanup;
101234313Sbapt	}
102234313Sbapt
103234313Sbapt	if (gelf_getehdr(elf, &elfhdr) == NULL) {
104234313Sbapt		ret = -1;
105234313Sbapt		warn("getehdr() failed: %s.", elf_errmsg(-1));
106234313Sbapt		goto cleanup;
107234313Sbapt	}
108234313Sbapt
109234313Sbapt	while ((scn = elf_nextscn(elf, scn)) != NULL) {
110234313Sbapt		if (gelf_getshdr(scn, &shdr) != &shdr) {
111234313Sbapt			ret = -1;
112234313Sbapt			warn("getshdr() failed: %s.", elf_errmsg(-1));
113234313Sbapt			goto cleanup;
114234313Sbapt		}
115234313Sbapt
116234313Sbapt		if (shdr.sh_type == SHT_NOTE)
117234313Sbapt			break;
118234313Sbapt	}
119234313Sbapt
120234313Sbapt	if (scn == NULL) {
121234313Sbapt		ret = -1;
122234351Sbapt		warn("failed to get the note section");
123234313Sbapt		goto cleanup;
124234313Sbapt	}
125234313Sbapt
126234313Sbapt	data = elf_getdata(scn, NULL);
127234313Sbapt	src = data->d_buf;
128234315Sbapt	for (;;) {
129234313Sbapt		memcpy(&note, src, sizeof(Elf_Note));
130234313Sbapt		src += sizeof(Elf_Note);
131234313Sbapt		if (note.n_type == NT_VERSION)
132234313Sbapt			break;
133234313Sbapt		src += note.n_namesz + note.n_descsz;
134234313Sbapt	}
135234313Sbapt	osname = src;
136234313Sbapt	src += note.n_namesz;
137234313Sbapt	if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
138234313Sbapt		version = be32dec(src);
139234313Sbapt	else
140234313Sbapt		version = le32dec(src);
141234313Sbapt
142234313Sbapt	for (i = 0; osname[i] != '\0'; i++)
143234313Sbapt		osname[i] = (char)tolower(osname[i]);
144234313Sbapt
145234313Sbapt	snprintf(dest, sz, "%s:%d:%s:%s",
146234322Sbapt	    osname, version / 100000,
147234322Sbapt	    elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
148234313Sbapt	    elf_corres_to_string(wordsize_corres,
149234322Sbapt	    (int)elfhdr.e_ident[EI_CLASS]));
150234313Sbapt
151234351Sbapt	ret = 0;
152234351Sbapt
153234313Sbapt	switch (elfhdr.e_machine) {
154234351Sbapt	case EM_ARM:
155234351Sbapt		snprintf(dest + strlen(dest), sz - strlen(dest),
156234351Sbapt		    ":%s:%s:%s", elf_corres_to_string(endian_corres,
157234351Sbapt		    (int)elfhdr.e_ident[EI_DATA]),
158234351Sbapt		    (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
159234351Sbapt		    "eabi" : "oabi",
160234351Sbapt		    (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
161234351Sbapt		    "softfp" : "vfp");
162234351Sbapt		break;
163234351Sbapt	case EM_MIPS:
164234351Sbapt		/*
165234351Sbapt		 * this is taken from binutils sources:
166234351Sbapt		 * include/elf/mips.h
167234351Sbapt		 * mapping is figured out from binutils:
168234351Sbapt		 * gas/config/tc-mips.c
169234351Sbapt		 */
170234351Sbapt		switch (elfhdr.e_flags & EF_MIPS_ABI) {
171234351Sbapt		case E_MIPS_ABI_O32:
172234351Sbapt			abi = "o32";
173234313Sbapt			break;
174234351Sbapt		case E_MIPS_ABI_N32:
175234351Sbapt			abi = "n32";
176234313Sbapt			break;
177234351Sbapt		default:
178234351Sbapt			if (elfhdr.e_ident[EI_DATA] ==
179234351Sbapt			    ELFCLASS32)
180234351Sbapt				abi = "o32";
181234351Sbapt			else if (elfhdr.e_ident[EI_DATA] ==
182234351Sbapt			    ELFCLASS64)
183234351Sbapt				abi = "n64";
184234351Sbapt			break;
185234351Sbapt		}
186234351Sbapt		snprintf(dest + strlen(dest), sz - strlen(dest),
187234351Sbapt		    ":%s:%s", elf_corres_to_string(endian_corres,
188234351Sbapt		    (int)elfhdr.e_ident[EI_DATA]), abi);
189234351Sbapt		break;
190234313Sbapt	}
191234313Sbapt
192234313Sbaptcleanup:
193234313Sbapt	if (elf != NULL)
194234313Sbapt		elf_end(elf);
195234313Sbapt
196234313Sbapt	close(fd);
197234313Sbapt	return (ret);
198234313Sbapt}
199234313Sbapt
200234313Sbaptstatic int
201234313Sbaptextract_pkg_static(int fd, char *p, int sz)
202234313Sbapt{
203234313Sbapt	struct archive *a;
204234313Sbapt	struct archive_entry *ae;
205234313Sbapt	char *end;
206234313Sbapt	int ret, r;
207234313Sbapt
208234351Sbapt	ret = -1;
209234313Sbapt	a = archive_read_new();
210234351Sbapt	if (a == NULL) {
211234351Sbapt		warn("archive_read_new");
212234351Sbapt		return (ret);
213234351Sbapt	}
214234313Sbapt	archive_read_support_compression_all(a);
215234313Sbapt	archive_read_support_format_tar(a);
216234313Sbapt
217234351Sbapt	if (lseek(fd, 0, 0) == -1) {
218234351Sbapt		warn("lseek");
219234351Sbapt		goto cleanup;
220234351Sbapt	}
221234313Sbapt
222234313Sbapt	if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
223234351Sbapt		warnx("archive_read_open_fd: %s", archive_error_string(a));
224234313Sbapt		goto cleanup;
225234313Sbapt	}
226234313Sbapt
227234313Sbapt	ae = NULL;
228234313Sbapt	while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
229234313Sbapt		end = strrchr(archive_entry_pathname(ae), '/');
230234313Sbapt		if (end == NULL)
231234313Sbapt			continue;
232234313Sbapt
233234313Sbapt		if (strcmp(end, "/pkg-static") == 0) {
234234313Sbapt			r = archive_read_extract(a, ae,
235234322Sbapt			    ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM |
236234322Sbapt			    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL |
237234322Sbapt			    ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR);
238234351Sbapt			strlcpy(p, archive_entry_pathname(ae), sz);
239234313Sbapt			break;
240234313Sbapt		}
241234313Sbapt	}
242234313Sbapt
243234351Sbapt	if (r == ARCHIVE_OK)
244234351Sbapt		ret = 0;
245234351Sbapt	else
246234313Sbapt		warnx("fail to extract pkg-static");
247234313Sbapt
248234313Sbaptcleanup:
249234313Sbapt	archive_read_finish(a);
250234322Sbapt	return (ret);
251234313Sbapt
252234313Sbapt}
253234313Sbapt
254234313Sbaptstatic int
255234313Sbaptinstall_pkg_static(char *path, char *pkgpath)
256234313Sbapt{
257234313Sbapt	int pstat;
258234313Sbapt	pid_t pid;
259234313Sbapt
260234313Sbapt	switch ((pid = fork())) {
261234351Sbapt	case -1:
262234351Sbapt		return (-1);
263234351Sbapt	case 0:
264234351Sbapt		execl(path, "pkg-static", "add", pkgpath, (char *)NULL);
265234351Sbapt		_exit(1);
266234351Sbapt	default:
267234351Sbapt		break;
268234313Sbapt	}
269234313Sbapt
270234351Sbapt	while (waitpid(pid, &pstat, 0) == -1)
271234313Sbapt		if (errno != EINTR)
272234313Sbapt			return (-1);
273234313Sbapt
274234351Sbapt	if (WEXITSTATUS(pstat))
275234351Sbapt		return (WEXITSTATUS(pstat));
276234351Sbapt	else if (WIFSIGNALED(pstat))
277234351Sbapt		return (128 & (WTERMSIG(pstat)));
278234351Sbapt	return (pstat);
279234313Sbapt}
280234313Sbapt
281234313Sbaptstatic int
282234313Sbaptbootstrap_pkg(void)
283234313Sbapt{
284234313Sbapt	FILE *remote;
285234313Sbapt	char url[MAXPATHLEN];
286234313Sbapt	char abi[BUFSIZ];
287234313Sbapt	char tmppkg[MAXPATHLEN];
288234313Sbapt	char buf[10240];
289234313Sbapt	char pkgstatic[MAXPATHLEN];
290234313Sbapt	int fd, retry, ret;
291234351Sbapt	struct url_stat st;
292234313Sbapt	off_t done, r;
293234351Sbapt	time_t begin_dl;
294234351Sbapt	time_t now;
295234351Sbapt	time_t last;
296234313Sbapt
297234313Sbapt	done = 0;
298234351Sbapt	last = 0;
299234351Sbapt	ret = -1;
300234313Sbapt	remote = NULL;
301234313Sbapt
302234351Sbapt	printf("Bootstrapping pkg please wait\n");
303234313Sbapt
304234313Sbapt	if (pkg_get_myabi(abi, MAXPATHLEN) != 0) {
305234351Sbapt		warnx("failed to determine the system ABI");
306234322Sbapt		return (-1);
307234313Sbapt	}
308234313Sbapt
309234351Sbapt	if (getenv("PACKAGESITE") != NULL)
310234351Sbapt		snprintf(url, MAXPATHLEN, "%s/pkg.txz", getenv("PACKAGESITE"));
311234351Sbapt	else
312234313Sbapt		snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz",
313234313Sbapt		    getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL,
314234313Sbapt		    getenv("ABI") ? getenv("ABI") : abi);
315234313Sbapt
316234313Sbapt	snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
317234351Sbapt	    getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
318234313Sbapt
319234313Sbapt	if ((fd = mkstemp(tmppkg)) == -1) {
320234313Sbapt		warn("mkstemp()");
321234322Sbapt		return (-1);
322234313Sbapt	}
323234313Sbapt
324234351Sbapt	retry = 3;
325234351Sbapt	do {
326234313Sbapt		remote = fetchXGetURL(url, &st, "");
327234351Sbapt		if (remote == NULL)
328234313Sbapt			sleep(1);
329234351Sbapt	} while (remote == NULL && retry-- > 0);
330234313Sbapt
331234351Sbapt	if (remote == NULL)
332234351Sbapt		goto fetchfail;
333234351Sbapt
334234313Sbapt	begin_dl = time(NULL);
335234313Sbapt	while (done < st.size) {
336234313Sbapt		if ((r = fread(buf, 1, sizeof(buf), remote)) < 1)
337234313Sbapt			break;
338234313Sbapt
339234313Sbapt		if (write(fd, buf, r) != r) {
340234313Sbapt			warn("write()");
341234313Sbapt			goto cleanup;
342234313Sbapt		}
343234313Sbapt
344234313Sbapt		done += r;
345234313Sbapt		now = time(NULL);
346234351Sbapt		if (now > last || done == st.size)
347234313Sbapt			last = now;
348234313Sbapt	}
349234313Sbapt
350234351Sbapt	if (ferror(remote))
351234351Sbapt		goto fetchfail;
352234313Sbapt
353234313Sbapt	if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
354234313Sbapt		ret = install_pkg_static(pkgstatic, tmppkg);
355234313Sbapt
356234351Sbapt	goto cleanup;
357234351Sbapt
358234351Sbaptfetchfail:
359234351Sbapt	warnx("Error fetching %s: %s", url, fetchLastErrString);
360234351Sbapt
361234313Sbaptcleanup:
362234313Sbapt	close(fd);
363234313Sbapt	unlink(tmppkg);
364234313Sbapt
365234351Sbapt	return (ret);
366234313Sbapt}
367234313Sbapt
368234313Sbaptint
369234322Sbaptmain(__unused int argc, char *argv[])
370234313Sbapt{
371234313Sbapt	char pkgpath[MAXPATHLEN];
372234313Sbapt
373234313Sbapt	snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
374234322Sbapt	    getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
375234313Sbapt
376234313Sbapt	if (access(pkgpath, X_OK) == -1)
377234351Sbapt		if (bootstrap_pkg() != 0)
378234351Sbapt			exit(EXIT_FAILURE);
379234313Sbapt
380234313Sbapt	execv(pkgpath, argv);
381234313Sbapt
382234351Sbapt	/* NOT REACHED */
383234322Sbapt	return (EXIT_FAILURE);
384234313Sbapt}
385