pkg.c revision 234315
1/*-
2 * Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/usr.sbin/pkg/pkg.c 234315 2012-04-15 16:00:32Z bapt $");
29
30#include <sys/types.h>
31#include <sys/wait.h>
32#include <sys/param.h>
33#include <sys/elf_common.h>
34#include <sys/endian.h>
35
36#include <archive.h>
37#include <archive_entry.h>
38#include <ctype.h>
39#include <err.h>
40#include <errno.h>
41#include <fcntl.h>
42#include <gelf.h>
43#include <stdlib.h>
44#include <stdio.h>
45#include <string.h>
46#include <time.h>
47#include <unistd.h>
48#include <fetch.h>
49
50#include "elf_tables.h"
51
52#define _LOCALBASE "/usr/local"
53#define _PKGS_URL "http://pkgbeta.FreeBSD.org"
54#define _DEFAULT_TMP "/tmp"
55
56static const char *
57elf_corres_to_string(struct _elf_corres* m, int e)
58{
59	int i;
60
61	for (i = 0; m[i].string != NULL; i++)
62		if (m[i].elf_nb == e)
63			return (m[i].string);
64
65	return ("unknown");
66}
67
68static int
69pkg_get_myabi(char *dest, size_t sz)
70{
71	Elf *elf;
72	GElf_Ehdr elfhdr;
73	GElf_Shdr shdr;
74	Elf_Data *data;
75	Elf_Note note;
76	Elf_Scn *scn;
77	char *src, *osname;
78	const char *abi;
79	int fd, i, ret;
80	uint32_t version;
81
82	version = 0;
83	ret = 0;
84	scn = NULL;
85	abi = NULL;
86
87	if (elf_version(EV_CURRENT) == EV_NONE) {
88		warnx("ELF library initialization failed: %s", elf_errmsg(-1));
89		return -1;
90	}
91
92	if ((fd = open("/bin/sh", O_RDONLY)) < 0) {
93		warn("open()");
94		return -1;
95	}
96
97	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
98		ret = -1;
99		warnx("elf_begin() failed: %s.", elf_errmsg(-1));
100		goto cleanup;
101	}
102
103	if (gelf_getehdr(elf, &elfhdr) == NULL) {
104		ret = -1;
105		warn("getehdr() failed: %s.", elf_errmsg(-1));
106		goto cleanup;
107	}
108
109	while ((scn = elf_nextscn(elf, scn)) != NULL) {
110		if (gelf_getshdr(scn, &shdr) != &shdr) {
111			ret = -1;
112			warn("getshdr() failed: %s.", elf_errmsg(-1));
113			goto cleanup;
114		}
115
116		if (shdr.sh_type == SHT_NOTE)
117			break;
118	}
119
120	if (scn == NULL) {
121		ret = -1;
122		warn("fail to get the note section");
123		goto cleanup;
124	}
125
126	data = elf_getdata(scn, NULL);
127	src = data->d_buf;
128	for (;;) {
129		memcpy(&note, src, sizeof(Elf_Note));
130		src += sizeof(Elf_Note);
131		if (note.n_type == NT_VERSION)
132			break;
133		src += note.n_namesz + note.n_descsz;
134	}
135	osname = src;
136	src += note.n_namesz;
137	if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
138		version = be32dec(src);
139	else
140		version = le32dec(src);
141
142	for (i = 0; osname[i] != '\0'; i++)
143		osname[i] = (char)tolower(osname[i]);
144
145	snprintf(dest, sz, "%s:%d:%s:%s",
146	    osname,
147	    version / 100000,
148	    elf_corres_to_string(mach_corres, (int) elfhdr.e_machine),
149	    elf_corres_to_string(wordsize_corres,
150	        (int)elfhdr.e_ident[EI_CLASS]));
151
152	switch (elfhdr.e_machine) {
153		case EM_ARM:
154			snprintf(dest + strlen(dest), sz - strlen(dest),
155			    ":%s:%s:%s",
156			    elf_corres_to_string(endian_corres,
157			        (int) elfhdr.e_ident[EI_DATA]),
158			    (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
159			        "eabi" : "oabi",
160			    (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
161			        "softfp" : "vfp");
162			break;
163		case EM_MIPS:
164			/*
165			 * this is taken from binutils sources:
166			 * include/elf/mips.h
167			 * mapping is figured out from binutils:
168			 * gas/config/tc-mips.c
169			 */
170			switch (elfhdr.e_flags & EF_MIPS_ABI) {
171				case E_MIPS_ABI_O32:
172					abi = "o32";
173					break;
174				case E_MIPS_ABI_N32:
175					abi = "n32";
176					break;
177				default:
178					if (elfhdr.e_ident[EI_DATA] ==
179					    ELFCLASS32)
180						abi = "o32";
181					else if (elfhdr.e_ident[EI_DATA] ==
182					    ELFCLASS64)
183						abi = "n64";
184					break;
185			}
186			snprintf(dest + strlen(dest), sz - strlen(dest),
187			    ":%s:%s",
188			    elf_corres_to_string(endian_corres,
189			        (int) elfhdr.e_ident[EI_DATA]),
190			    abi);
191			break;
192	}
193
194cleanup:
195	if (elf != NULL)
196		elf_end(elf);
197
198	close(fd);
199	return (ret);
200}
201
202static int
203extract_pkg_static(int fd, char *p, int sz)
204{
205	struct archive *a;
206	struct archive_entry *ae;
207	char *end;
208	int ret, r;
209
210	ret = 0;
211	a = archive_read_new();
212	archive_read_support_compression_all(a);
213	archive_read_support_format_tar(a);
214
215	lseek(fd, 0, 0);
216
217	if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
218		warnx("archive_read_open_fd: %s",
219		    archive_error_string(a));
220		ret = -1;
221		goto cleanup;
222	}
223
224	ae = NULL;
225	while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
226		end = strrchr(archive_entry_pathname(ae), '/');
227		if (end == NULL)
228			continue;
229
230		if (strcmp(end, "/pkg-static") == 0) {
231			r = archive_read_extract(a, ae,
232			    ARCHIVE_EXTRACT_OWNER |ARCHIVE_EXTRACT_PERM|
233			    ARCHIVE_EXTRACT_TIME  |ARCHIVE_EXTRACT_ACL |
234			    ARCHIVE_EXTRACT_FFLAGS|ARCHIVE_EXTRACT_XATTR);
235			snprintf(p, sz, archive_entry_pathname(ae));
236			break;
237		}
238	}
239
240	if (r != ARCHIVE_OK) {
241		warnx("fail to extract pkg-static");
242		ret = -1;
243	}
244
245cleanup:
246	archive_read_finish(a);
247	return ret;
248
249}
250
251static int
252install_pkg_static(char *path, char *pkgpath)
253{
254	int pstat;
255	pid_t pid;
256
257	switch ((pid = fork())) {
258		case -1:
259			return (-1);
260		case 0:
261			execl(path, "pkg-static", "add", pkgpath, (char *)NULL);
262			_exit(1); /* NOT REACHED */
263		default:
264			break;
265	}
266
267	while (waitpid(pid, &pstat, 0) == -1) {
268		if (errno != EINTR)
269			return (-1);
270	}
271
272	return (WEXITSTATUS(pstat));
273}
274
275static int
276bootstrap_pkg(void)
277{
278	struct url_stat st;
279	FILE *remote;
280	time_t begin_dl;
281	time_t now;
282	time_t last = 0;
283	char url[MAXPATHLEN];
284	char abi[BUFSIZ];
285	char tmppkg[MAXPATHLEN];
286	char buf[10240];
287	char pkgstatic[MAXPATHLEN];
288	int fd, retry, ret;
289	off_t done, r;
290
291	done = 0;
292	ret = 0;
293	retry = 3;
294	remote = NULL;
295
296	printf("Bootstraping pkg please wait\n");
297
298	if (pkg_get_myabi(abi, MAXPATHLEN) != 0) {
299		warnx("fail to determine my abi");
300		return -1;
301	}
302
303	if (getenv("PACKAGESITE") != NULL) {
304		snprintf(url, MAXPATHLEN, "%s/pkg.txz",
305		    getenv("PACKAGESITE"));
306	} else {
307		snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz",
308		    getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL,
309		    getenv("ABI") ? getenv("ABI") : abi);
310	}
311
312	snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
313	    getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp");
314
315	if ((fd = mkstemp(tmppkg)) == -1) {
316		warn("mkstemp()");
317		return -1;
318	}
319
320	while (remote == NULL) {
321		remote = fetchXGetURL(url, &st, "");
322		if (remote == NULL) {
323			--retry;
324			if (retry == 0) {
325				warnx("Error fetching %s: %s", url,
326				    fetchLastErrString);
327				ret = 1;
328				goto cleanup;
329			}
330			sleep(1);
331		}
332	}
333
334	begin_dl = time(NULL);
335	while (done < st.size) {
336		if ((r = fread(buf, 1, sizeof(buf), remote)) < 1)
337			break;
338
339		if (write(fd, buf, r) != r) {
340			warn("write()");
341			ret = -1;
342			goto cleanup;
343		}
344
345		done += r;
346		now = time(NULL);
347		if (now > last || done == st.size) {
348			last = now;
349		}
350	}
351
352	if (ferror(remote)) {
353		warnx("Error fetching %s: %s", url,
354		    fetchLastErrString);
355		ret = 1;
356		goto cleanup;
357	}
358
359	if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
360		ret = install_pkg_static(pkgstatic, tmppkg);
361
362cleanup:
363	close(fd);
364	unlink(tmppkg);
365
366	return 0;
367}
368
369int
370main(__unused int argc, char * argv[])
371{
372	char pkgpath[MAXPATHLEN];
373
374	snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
375	    getenv("LOCALBASE") ? getenv("LOCALBASE"): _LOCALBASE);
376
377	if (access(pkgpath, X_OK) == -1)
378		bootstrap_pkg();
379
380	execv(pkgpath, argv);
381
382	return (EXIT_SUCCESS);
383}
384