xref: /freebsd/usr.sbin/pkg/pkg.c (revision 6e660824a82f590542932de52f128db584029893)
1 /*-
2  * Copyright (c) 2012-2013 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$");
29 
30 #include <sys/param.h>
31 #include <sys/wait.h>
32 
33 #include <archive.h>
34 #include <archive_entry.h>
35 #include <err.h>
36 #include <errno.h>
37 #include <fetch.h>
38 #include <paths.h>
39 #include <stdbool.h>
40 #include <stdlib.h>
41 #include <stdio.h>
42 #include <string.h>
43 #include <time.h>
44 #include <unistd.h>
45 
46 #include "dns_utils.h"
47 #include "config.h"
48 
49 static int
50 extract_pkg_static(int fd, char *p, int sz)
51 {
52 	struct archive *a;
53 	struct archive_entry *ae;
54 	char *end;
55 	int ret, r;
56 
57 	ret = -1;
58 	a = archive_read_new();
59 	if (a == NULL) {
60 		warn("archive_read_new");
61 		return (ret);
62 	}
63 	archive_read_support_filter_all(a);
64 	archive_read_support_format_tar(a);
65 
66 	if (lseek(fd, 0, 0) == -1) {
67 		warn("lseek");
68 		goto cleanup;
69 	}
70 
71 	if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
72 		warnx("archive_read_open_fd: %s", archive_error_string(a));
73 		goto cleanup;
74 	}
75 
76 	ae = NULL;
77 	while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
78 		end = strrchr(archive_entry_pathname(ae), '/');
79 		if (end == NULL)
80 			continue;
81 
82 		if (strcmp(end, "/pkg-static") == 0) {
83 			r = archive_read_extract(a, ae,
84 			    ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM |
85 			    ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL |
86 			    ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR);
87 			strlcpy(p, archive_entry_pathname(ae), sz);
88 			break;
89 		}
90 	}
91 
92 	if (r == ARCHIVE_OK)
93 		ret = 0;
94 	else
95 		warnx("fail to extract pkg-static");
96 
97 cleanup:
98 	archive_read_free(a);
99 	return (ret);
100 
101 }
102 
103 static int
104 install_pkg_static(char *path, char *pkgpath)
105 {
106 	int pstat;
107 	pid_t pid;
108 
109 	switch ((pid = fork())) {
110 	case -1:
111 		return (-1);
112 	case 0:
113 		execl(path, "pkg-static", "add", pkgpath, (char *)NULL);
114 		_exit(1);
115 	default:
116 		break;
117 	}
118 
119 	while (waitpid(pid, &pstat, 0) == -1)
120 		if (errno != EINTR)
121 			return (-1);
122 
123 	if (WEXITSTATUS(pstat))
124 		return (WEXITSTATUS(pstat));
125 	else if (WIFSIGNALED(pstat))
126 		return (128 & (WTERMSIG(pstat)));
127 	return (pstat);
128 }
129 
130 static int
131 bootstrap_pkg(void)
132 {
133 	struct url *u;
134 	FILE *remote;
135 	FILE *config;
136 	char *site;
137 	struct dns_srvinfo *mirrors, *current;
138 	/* To store _https._tcp. + hostname + \0 */
139 	char zone[MAXHOSTNAMELEN + 13];
140 	char url[MAXPATHLEN];
141 	char conf[MAXPATHLEN];
142 	char tmppkg[MAXPATHLEN];
143 	const char *packagesite, *mirror_type;
144 	char buf[10240];
145 	char pkgstatic[MAXPATHLEN];
146 	int fd, retry, ret, max_retry;
147 	struct url_stat st;
148 	off_t done, r;
149 	time_t now;
150 	time_t last;
151 
152 	done = 0;
153 	last = 0;
154 	max_retry = 3;
155 	ret = -1;
156 	remote = NULL;
157 	config = NULL;
158 	current = mirrors = NULL;
159 
160 	printf("Bootstrapping pkg please wait\n");
161 
162 	if (config_string(PACKAGESITE, &packagesite) != 0) {
163 		warnx("No PACKAGESITE defined");
164 		return (-1);
165 	}
166 	if (config_string(MIRROR_TYPE, &mirror_type) != 0) {
167 		warnx("No MIRROR_TYPE defined");
168 		return (-1);
169 	}
170 	snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", packagesite);
171 
172 	snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
173 	    getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
174 
175 	if ((fd = mkstemp(tmppkg)) == -1) {
176 		warn("mkstemp()");
177 		return (-1);
178 	}
179 
180 	retry = max_retry;
181 
182 	u = fetchParseURL(url);
183 	while (remote == NULL) {
184 		if (retry == max_retry) {
185 			if (strcmp(u->scheme, "file") != 0 &&
186 			    strcasecmp(mirror_type, "srv") == 0) {
187 				snprintf(zone, sizeof(zone),
188 				    "_%s._tcp.%s", u->scheme, u->host);
189 				mirrors = dns_getsrvinfo(zone);
190 				current = mirrors;
191 			}
192 		}
193 
194 		if (mirrors != NULL)
195 			strlcpy(u->host, current->host, sizeof(u->host));
196 
197 		remote = fetchXGet(u, &st, "");
198 		if (remote == NULL) {
199 			--retry;
200 			if (retry <= 0)
201 				goto fetchfail;
202 			if (mirrors == NULL) {
203 				sleep(1);
204 			} else {
205 				current = current->next;
206 				if (current == NULL)
207 					current = mirrors;
208 			}
209 		}
210 	}
211 
212 	if (remote == NULL)
213 		goto fetchfail;
214 
215 	while (done < st.size) {
216 		if ((r = fread(buf, 1, sizeof(buf), remote)) < 1)
217 			break;
218 
219 		if (write(fd, buf, r) != r) {
220 			warn("write()");
221 			goto cleanup;
222 		}
223 
224 		done += r;
225 		now = time(NULL);
226 		if (now > last || done == st.size)
227 			last = now;
228 	}
229 
230 	if (ferror(remote))
231 		goto fetchfail;
232 
233 	if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
234 		ret = install_pkg_static(pkgstatic, tmppkg);
235 
236 	snprintf(conf, MAXPATHLEN, "%s/etc/pkg.conf",
237 	    getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
238 
239 	if (access(conf, R_OK) == -1) {
240 		site = strrchr(url, '/');
241 		if (site == NULL)
242 			goto cleanup;
243 		site[0] = '\0';
244 		site = strrchr(url, '/');
245 		if (site == NULL)
246 			goto cleanup;
247 		site[0] = '\0';
248 
249 		config = fopen(conf, "w+");
250 		if (config == NULL)
251 			goto cleanup;
252 		fprintf(config, "packagesite: %s\n", url);
253 		fclose(config);
254 	}
255 
256 	goto cleanup;
257 
258 fetchfail:
259 	warnx("Error fetching %s: %s", url, fetchLastErrString);
260 	fprintf(stderr, "A pre-built version of pkg could not be found for your system.\n");
261 	fprintf(stderr, "Consider changing PACKAGESITE or installing it from ports: 'ports-mgmt/pkg'.\n");
262 
263 cleanup:
264 	if (remote != NULL)
265 		fclose(remote);
266 	close(fd);
267 	unlink(tmppkg);
268 
269 	return (ret);
270 }
271 
272 static const char confirmation_message[] =
273 "The package management tool is not yet installed on your system.\n"
274 "Do you want to fetch and install it now? [y/N]: ";
275 
276 static int
277 pkg_query_yes_no(void)
278 {
279 	int ret, c;
280 
281 	c = getchar();
282 
283 	if (c == 'y' || c == 'Y')
284 		ret = 1;
285 	else
286 		ret = 0;
287 
288 	while (c != '\n' && c != EOF)
289 		c = getchar();
290 
291 	return (ret);
292 }
293 
294 int
295 main(__unused int argc, char *argv[])
296 {
297 	char pkgpath[MAXPATHLEN];
298 	bool yes = false;
299 
300 	snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
301 	    getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
302 
303 	if (access(pkgpath, X_OK) == -1) {
304 		/*
305 		 * To allow 'pkg -N' to be used as a reliable test for whether
306 		 * a system is configured to use pkg, don't bootstrap pkg
307 		 * when that argument is given as argv[1].
308 		 */
309 		if (argv[1] != NULL && strcmp(argv[1], "-N") == 0)
310 			errx(EXIT_FAILURE, "pkg is not installed");
311 
312 		/*
313 		 * Do not ask for confirmation if either of stdin or stdout is
314 		 * not tty. Check the environment to see if user has answer
315 		 * tucked in there already.
316 		 */
317 		config_init();
318 		config_bool(ASSUME_ALWAYS_YES, &yes);
319 		if (!yes) {
320 			printf("%s", confirmation_message);
321 			if (!isatty(fileno(stdin)))
322 				exit(EXIT_FAILURE);
323 
324 			if (pkg_query_yes_no() == 0)
325 				exit(EXIT_FAILURE);
326 		}
327 		if (bootstrap_pkg() != 0)
328 			exit(EXIT_FAILURE);
329 		config_finish();
330 	}
331 
332 	execv(pkgpath, argv);
333 
334 	/* NOT REACHED */
335 	return (EXIT_FAILURE);
336 }
337