util.c 38.1 KB
Newer Older
Judd Vinet's avatar
Judd Vinet committed
1
2
/*
 *  util.c
3
 *
4
 *  Copyright (c) 2006-2020 Pacman Development Team <pacman-dev@archlinux.org>
5
 *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
6
7
8
9
 *  Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
 *  Copyright (c) 2005 by Christian Hamar <krics@linuxforum.hu>
 *  Copyright (c) 2006 by David Kimpe <dnaku@frugalware.org>
 *  Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
Dan McGee's avatar
Dan McGee committed
10
 *
Judd Vinet's avatar
Judd Vinet committed
11
12
13
14
15
16
17
18
19
20
21
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
22
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Judd Vinet's avatar
Judd Vinet committed
23
24
25
 */

#include <stdlib.h>
Dan McGee's avatar
Dan McGee committed
26
#include <unistd.h>
Judd Vinet's avatar
Judd Vinet committed
27
28
29
#include <ctype.h>
#include <dirent.h>
#include <time.h>
Dan McGee's avatar
Dan McGee committed
30
#include <errno.h>
31
#include <limits.h>
Xavier Chantry's avatar
Xavier Chantry committed
32
#include <sys/wait.h>
33
#include <sys/socket.h>
34
#include <fnmatch.h>
35
#include <poll.h>
Dan McGee's avatar
Dan McGee committed
36
37
38
39

/* libarchive */
#include <archive.h>
#include <archive_entry.h>
40

41
42
#ifdef HAVE_LIBSSL
#include <openssl/md5.h>
Dan McGee's avatar
Dan McGee committed
43
#include <openssl/sha.h>
44
45
#endif

46
47
48
49
50
#ifdef HAVE_LIBNETTLE
#include <nettle/md5.h>
#include <nettle/sha2.h>
#endif

51
52
/* libalpm */
#include "util.h"
Judd Vinet's avatar
Judd Vinet committed
53
#include "log.h"
54
#include "libarchive-compat.h"
Judd Vinet's avatar
Judd Vinet committed
55
#include "alpm.h"
56
#include "alpm_list.h"
Xavier Chantry's avatar
Xavier Chantry committed
57
#include "handle.h"
58
#include "trans.h"
Judd Vinet's avatar
Judd Vinet committed
59

60
#ifndef HAVE_STRSEP
61
62
63
64
65
66
67
68
69
70
/** Extracts tokens from a string.
 * Replaces strset which is not portable (missing on Solaris).
 * Copyright (c) 2001 by François Gouget <fgouget_at_codeweavers.com>
 * Modifies str to point to the first character after the token if one is
 * found, or NULL if one is not.
 * @param str string containing delimited tokens to parse
 * @param delim character delimiting tokens in str
 * @return pointer to the first token in str if str is not NULL, NULL if
 * str is NULL
 */
Florian Pritz's avatar
Florian Pritz committed
71
char *strsep(char **str, const char *delims)
72
{
Florian Pritz's avatar
Florian Pritz committed
73
	char *token;
74

75
	if(*str == NULL) {
76
77
78
79
		/* No more tokens */
		return NULL;
	}

80
81
82
83
	token = *str;
	while(**str != '\0') {
		if(strchr(delims, **str) != NULL) {
			**str = '\0';
84
85
86
87
88
89
			(*str)++;
			return token;
		}
		(*str)++;
	}
	/* There is no other token */
90
	*str = NULL;
91
92
93
94
	return token;
}
#endif

Aaron Griffin's avatar
Aaron Griffin committed
95
int _alpm_makepath(const char *path)
96
{
97
	return _alpm_makepath_mode(path, 0755);
98
99
}

100
101
102
103
104
/** Creates a directory, including parents if needed, similar to 'mkdir -p'.
 * @param path directory path to create
 * @param mode permission mode for created directories
 * @return 0 on success, 1 on error
 */
105
int _alpm_makepath_mode(const char *path, mode_t mode)
Judd Vinet's avatar
Judd Vinet committed
106
{
107
108
	char *ptr, *str;
	mode_t oldmask;
109
	int ret = 0;
Judd Vinet's avatar
Judd Vinet committed
110

111
112
113
114
115
116
117
118
	STRDUP(str, path, return 1);

	oldmask = umask(0000);

	for(ptr = str; *ptr; ptr++) {
		/* detect mid-path condition and zero length paths */
		if(*ptr != '/' || ptr == str || ptr[-1] == '/') {
			continue;
Judd Vinet's avatar
Judd Vinet committed
119
		}
120
121
122
123

		/* temporarily mask the end of the path */
		*ptr = '\0';

124
		if(mkdir(str, mode) < 0 && errno != EEXIST) {
125
126
127
128
129
130
			ret = 1;
			goto done;
		}

		/* restore path separator */
		*ptr = '/';
Judd Vinet's avatar
Judd Vinet committed
131
	}
132
133
134

	/* end of the string. add the full path. It will already exist when the path
	 * passed in has a trailing slash. */
135
	if(mkdir(str, mode) < 0 && errno != EEXIST) {
136
137
138
139
		ret = 1;
	}

done:
Judd Vinet's avatar
Judd Vinet committed
140
	umask(oldmask);
141
	free(str);
142
	return ret;
Judd Vinet's avatar
Judd Vinet committed
143
144
}

145
146
147
/** Copies a file.
 * @param src file path to copy from
 * @param dest file path to copy to
148
 * @return 0 on success, 1 on error
149
 */
Aaron Griffin's avatar
Aaron Griffin committed
150
int _alpm_copyfile(const char *src, const char *dest)
Judd Vinet's avatar
Judd Vinet committed
151
{
Dan McGee's avatar
Dan McGee committed
152
	char *buf;
153
154
155
	int in, out, ret = 1;
	ssize_t nread;
	struct stat st;
Judd Vinet's avatar
Judd Vinet committed
156

157
158
	MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);

159
	OPEN(in, src, O_RDONLY | O_CLOEXEC);
160
	do {
161
		out = open(dest, O_WRONLY | O_CREAT | O_BINARY | O_CLOEXEC, 0000);
162
163
164
	} while(out == -1 && errno == EINTR);
	if(in < 0 || out < 0) {
		goto cleanup;
Judd Vinet's avatar
Judd Vinet committed
165
166
	}

167
168
169
	if(fstat(in, &st) || fchmod(out, st.st_mode)) {
		goto cleanup;
	}
Dan McGee's avatar
Dan McGee committed
170

171
	/* do the actual file copy */
172
173
174
175
	while((nread = read(in, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
		ssize_t nwrite = 0;
		if(nread < 0) {
			continue;
176
		}
177
178
179
180
181
182
183
184
		do {
			nwrite = write(out, buf + nwrite, nread);
			if(nwrite >= 0) {
				nread -= nwrite;
			} else if(errno != EINTR) {
				goto cleanup;
			}
		} while(nread > 0);
Judd Vinet's avatar
Judd Vinet committed
185
	}
186
	ret = 0;
187

Dan McGee's avatar
Dan McGee committed
188
cleanup:
189
	free(buf);
190
	if(in >= 0) {
191
		close(in);
192
193
	}
	if(out >= 0) {
194
		close(out);
195
	}
196
	return ret;
Judd Vinet's avatar
Judd Vinet committed
197
198
}

199
/** Trim trailing newlines from a string (if any exist).
200
 * @param str a single line of text
201
 * @param len size of str, if known, else 0
202
203
 * @return the length of the trimmed string
 */
204
size_t _alpm_strip_newline(char *str, size_t len)
205
{
Dan McGee's avatar
Dan McGee committed
206
	if(*str == '\0') {
207
208
		return 0;
	}
209
210
211
	if(len == 0) {
		len = strlen(str);
	}
212
	while(len > 0 && str[len - 1] == '\n') {
213
214
215
216
217
218
219
		len--;
	}
	str[len] = '\0';

	return len;
}

220
/* Compression functions */
221

222
/** Open an archive for reading and perform the necessary boilerplate.
223
224
225
 * This takes care of creating the libarchive 'archive' struct, setting up
 * compression and format options, opening a file descriptor, setting up the
 * buffer size, and performing a stat on the path once opened.
226
227
 * On error, no file descriptor is opened, and the archive pointer returned
 * will be set to NULL.
228
229
230
231
232
233
234
235
236
237
238
239
 * @param handle the context handle
 * @param path the path of the archive to open
 * @param buf space for a stat buffer for the given path
 * @param archive pointer to place the created archive object
 * @param error error code to set on failure to open archive
 * @return -1 on failure, >=0 file descriptor on success
 */
int _alpm_open_archive(alpm_handle_t *handle, const char *path,
		struct stat *buf, struct archive **archive, alpm_errno_t error)
{
	int fd;
	size_t bufsize = ALPM_BUFFER_SIZE;
240
	errno = 0;
241
242
243
244
245

	if((*archive = archive_read_new()) == NULL) {
		RET_ERR(handle, ALPM_ERR_LIBARCHIVE, -1);
	}

246
	_alpm_archive_read_support_filter_all(*archive);
247
248
249
	archive_read_support_format_all(*archive);

	_alpm_log(handle, ALPM_LOG_DEBUG, "opening archive %s\n", path);
250
	OPEN(fd, path, O_RDONLY | O_CLOEXEC);
251
252
253
	if(fd < 0) {
		_alpm_log(handle, ALPM_LOG_ERROR,
				_("could not open file %s: %s\n"), path, strerror(errno));
254
		goto error;
255
256
257
258
259
	}

	if(fstat(fd, buf) != 0) {
		_alpm_log(handle, ALPM_LOG_ERROR,
				_("could not stat file %s: %s\n"), path, strerror(errno));
260
		goto error;
261
262
263
264
265
266
267
268
269
270
	}
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
	if(buf->st_blksize > ALPM_BUFFER_SIZE) {
		bufsize = buf->st_blksize;
	}
#endif

	if(archive_read_open_fd(*archive, fd, bufsize) != ARCHIVE_OK) {
		_alpm_log(handle, ALPM_LOG_ERROR, _("could not open file %s: %s\n"),
				path, archive_error_string(*archive));
271
		goto error;
272
273
274
	}

	return fd;
275
276

error:
277
	_alpm_archive_read_free(*archive);
278
279
	*archive = NULL;
	if(fd >= 0) {
280
		close(fd);
281
282
	}
	RET_ERR(handle, error, -1);
283
284
}

285
/** Unpack a specific file in an archive.
Dan McGee's avatar
Dan McGee committed
286
287
288
289
 * @param handle the context handle
 * @param archive the archive to unpack
 * @param prefix where to extract the files
 * @param filename a file within the archive to unpack
290
 * @return 0 on success, 1 on failure
K. Piche's avatar
K. Piche committed
291
 */
292
int _alpm_unpack_single(alpm_handle_t *handle, const char *archive,
Dan McGee's avatar
Dan McGee committed
293
		const char *prefix, const char *filename)
294
295
296
{
	alpm_list_t *list = NULL;
	int ret = 0;
Dan McGee's avatar
Dan McGee committed
297
	if(filename == NULL) {
298
		return 1;
299
	}
Dan McGee's avatar
Dan McGee committed
300
301
	list = alpm_list_add(list, (void *)filename);
	ret = _alpm_unpack(handle, archive, prefix, list, 1);
302
	alpm_list_free(list);
303
	return ret;
304
305
}

306
/** Unpack a list of files in an archive.
Dan McGee's avatar
Dan McGee committed
307
 * @param handle the context handle
308
 * @param path the archive to unpack
Dan McGee's avatar
Dan McGee committed
309
310
 * @param prefix where to extract the files
 * @param list a list of files within the archive to unpack or NULL for all
311
312
313
 * @param breakfirst break after the first entry found
 * @return 0 on success, 1 on failure
 */
314
int _alpm_unpack(alpm_handle_t *handle, const char *path, const char *prefix,
Dan McGee's avatar
Dan McGee committed
315
		alpm_list_t *list, int breakfirst)
Judd Vinet's avatar
Judd Vinet committed
316
{
317
	int ret = 0;
318
	mode_t oldmask;
319
	struct archive *archive;
320
	struct archive_entry *entry;
321
322
	struct stat buf;
	int fd, cwdfd;
323

324
325
326
	fd = _alpm_open_archive(handle, path, &buf, &archive, ALPM_ERR_PKG_OPEN);
	if(fd < 0) {
		return 1;
327
	}
328

329
	oldmask = umask(0022);
330
331

	/* save the cwd so we can restore it later */
332
	OPEN(cwdfd, ".", O_RDONLY | O_CLOEXEC);
333
	if(cwdfd < 0) {
334
		_alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
335
336
337
338
	}

	/* just in case our cwd was removed in the upgrade operation */
	if(chdir(prefix) != 0) {
339
		_alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
Dan McGee's avatar
Dan McGee committed
340
				prefix, strerror(errno));
341
342
343
344
		ret = 1;
		goto cleanup;
	}

345
	while(archive_read_next_header(archive, &entry) == ARCHIVE_OK) {
346
347
		const char *entryname;
		mode_t mode;
348

349
		entryname = archive_entry_pathname(entry);
350

351
352
		/* If specific files were requested, skip entries that don't match. */
		if(list) {
353
354
			char *entry_prefix = NULL;
			STRDUP(entry_prefix, entryname, ret = 1; goto cleanup);
355
			char *p = strstr(entry_prefix,"/");
356
			if(p) {
357
				*(p + 1) = '\0';
358
			}
Dan McGee's avatar
Dan McGee committed
359
360
			char *found = alpm_list_find_str(list, entry_prefix);
			free(entry_prefix);
361
			if(!found) {
362
				if(archive_read_data_skip(archive) != ARCHIVE_OK) {
363
364
365
366
					ret = 1;
					goto cleanup;
				}
				continue;
Xavier Chantry's avatar
Xavier Chantry committed
367
			} else {
368
				_alpm_log(handle, ALPM_LOG_DEBUG, "extracting: %s\n", entryname);
369
			}
Judd Vinet's avatar
Judd Vinet committed
370
		}
K. Piche's avatar
K. Piche committed
371

372
373
374
375
376
377
378
		mode = archive_entry_mode(entry);
		if(S_ISREG(mode)) {
			archive_entry_set_perm(entry, 0644);
		} else if(S_ISDIR(mode)) {
			archive_entry_set_perm(entry, 0755);
		}

K. Piche's avatar
K. Piche committed
379
		/* Extract the archive entry. */
380
		int readret = archive_read_extract(archive, entry, 0);
381
		if(readret == ARCHIVE_WARN) {
382
			/* operation succeeded but a non-critical error was encountered */
383
			_alpm_log(handle, ALPM_LOG_WARNING, _("warning given when extracting %s (%s)\n"),
384
					entryname, archive_error_string(archive));
385
		} else if(readret != ARCHIVE_OK) {
386
			_alpm_log(handle, ALPM_LOG_ERROR, _("could not extract %s (%s)\n"),
387
					entryname, archive_error_string(archive));
388
389
			ret = 1;
			goto cleanup;
Judd Vinet's avatar
Judd Vinet committed
390
391
		}

392
		if(breakfirst) {
393
			break;
394
		}
395
	}
396

397
398
cleanup:
	umask(oldmask);
399
	_alpm_archive_read_free(archive);
400
	close(fd);
401
402
403
404
405
	if(cwdfd >= 0) {
		if(fchdir(cwdfd) != 0) {
			_alpm_log(handle, ALPM_LOG_ERROR,
					_("could not restore working directory (%s)\n"), strerror(errno));
		}
406
		close(cwdfd);
407
	}
408

409
	return ret;
410
411
}

412
/** Determine if there are files in a directory.
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
 * @param handle the context handle
 * @param path the full absolute directory path
 * @param full_count whether to return an exact count of files
 * @return a file count if full_count is != 0, else >0 if directory has
 * contents, 0 if no contents, and -1 on error
 */
ssize_t _alpm_files_in_directory(alpm_handle_t *handle, const char *path,
		int full_count)
{
	ssize_t files = 0;
	struct dirent *ent;
	DIR *dir = opendir(path);

	if(!dir) {
		if(errno == ENOTDIR) {
			_alpm_log(handle, ALPM_LOG_DEBUG, "%s was not a directory\n", path);
		} else {
			_alpm_log(handle, ALPM_LOG_DEBUG, "could not read directory %s\n",
					path);
		}
		return -1;
	}
	while((ent = readdir(dir)) != NULL) {
		const char *name = ent->d_name;

		if(strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
			continue;
		}

		files++;

		if(!full_count) {
			break;
		}
	}

	closedir(dir);
	return files;
}

453
454
455
456
457
458
459
460
461
462
static int should_retry(int errnum)
{
	return errnum == EAGAIN
/* EAGAIN may be the same value as EWOULDBLOCK (POSIX.1) - prevent GCC warning */
#if EAGAIN != EWOULDBLOCK
	|| errnum == EWOULDBLOCK
#endif
	|| errnum == EINTR;
}

463
464
465
466
467
468
469
470
471
472
473
474
475
476
static int _alpm_chroot_write_to_child(alpm_handle_t *handle, int fd,
		char *buf, ssize_t *buf_size, ssize_t buf_limit,
		_alpm_cb_io out_cb, void *cb_ctx)
{
	ssize_t nwrite;

	if(*buf_size == 0) {
		/* empty buffer, ask the callback for more */
		if((*buf_size = out_cb(buf, buf_limit, cb_ctx)) == 0) {
			/* no more to write, close the pipe */
			return -1;
		}
	}

477
	nwrite = send(fd, buf, *buf_size, MSG_NOSIGNAL);
478
479
480
481
482

	if(nwrite != -1) {
		/* write was successful, remove the written data from the buffer */
		*buf_size -= nwrite;
		memmove(buf, buf + nwrite, *buf_size);
483
	} else if(should_retry(errno)) {
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
		/* nothing written, try again later */
	} else {
		_alpm_log(handle, ALPM_LOG_ERROR,
				_("unable to write to pipe (%s)\n"), strerror(errno));
		return -1;
	}

	return 0;
}

static void _alpm_chroot_process_output(alpm_handle_t *handle, const char *line)
{
	alpm_event_scriptlet_info_t event = {
		.type = ALPM_EVENT_SCRIPTLET_INFO,
		.line = line
	};
	alpm_logaction(handle, "ALPM-SCRIPTLET", "%s", line);
	EVENT(handle, &event);
}

static int _alpm_chroot_read_from_child(alpm_handle_t *handle, int fd,
		char *buf, ssize_t *buf_size, ssize_t buf_limit)
{
	ssize_t space = buf_limit - *buf_size - 2; /* reserve 2 for "\n\0" */
	ssize_t nread = read(fd, buf + *buf_size, space);
	if(nread > 0) {
		char *newline = memchr(buf + *buf_size, '\n', nread);
		*buf_size += nread;
		if(newline) {
			while(newline) {
				size_t linelen = newline - buf + 1;
				char old = buf[linelen];
				buf[linelen] = '\0';
				_alpm_chroot_process_output(handle, buf);
				buf[linelen] = old;

				*buf_size -= linelen;
				memmove(buf, buf + linelen, *buf_size);
				newline = memchr(buf, '\n', *buf_size);
			}
		} else if(nread == space) {
			/* we didn't read a full line, but we're out of space */
			strcpy(buf + *buf_size, "\n");
			_alpm_chroot_process_output(handle, buf);
			*buf_size = 0;
		}
	} else if(nread == 0) {
		/* end-of-file */
		if(*buf_size) {
			strcpy(buf + *buf_size, "\n");
			_alpm_chroot_process_output(handle, buf);
		}
		return -1;
537
	} else if(should_retry(errno)) {
538
539
540
541
542
543
544
545
546
547
548
549
550
551
		/* nothing read, try again */
	} else {
		/* read error */
		if(*buf_size) {
			strcpy(buf + *buf_size, "\n");
			_alpm_chroot_process_output(handle, buf);
		}
		_alpm_log(handle, ALPM_LOG_ERROR,
				_("unable to read from pipe (%s)\n"), strerror(errno));
		return -1;
	}
	return 0;
}

552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
static void _alpm_reset_signals(void)
{
	/* reset POSIX defined signals (see signal.h) */
	/* there are likely more but there is no easy way
	 * to get the full list of valid signals */
	int *i, signals[] = {
		SIGABRT, SIGALRM, SIGBUS, SIGCHLD, SIGCONT, SIGFPE, SIGHUP, SIGILL,
		SIGINT, SIGKILL, SIGPIPE, SIGQUIT, SIGSEGV, SIGSTOP, SIGTERM, SIGTSTP,
		SIGTTIN, SIGTTOU, SIGUSR1, SIGUSR2, SIGPOLL, SIGPROF, SIGSYS, SIGTRAP,
		SIGURG, SIGVTALRM, SIGXCPU, SIGXFSZ,
		0
	};
	struct sigaction def;
	def.sa_handler = SIG_DFL;
	for(i = signals; *i; i++) {
		sigaction(*i, &def, NULL);
	}
}

571
572
573
574
/** Execute a command with arguments in a chroot.
 * @param handle the context handle
 * @param cmd command to execute
 * @param argv arguments to pass to cmd
575
576
 * @param stdin_cb callback to provide input to the chroot on stdin
 * @param stdin_ctx context to be passed to @a stdin_cb
577
578
 * @return 0 on success, 1 on error
 */
579
580
int _alpm_run_chroot(alpm_handle_t *handle, const char *cmd, char *const argv[],
		_alpm_cb_io stdin_cb, void *stdin_ctx)
Xavier Chantry's avatar
Xavier Chantry committed
581
582
{
	pid_t pid;
583
584
	int child2parent_pipefd[2], parent2child_pipefd[2];
	int cwdfd;
Xavier Chantry's avatar
Xavier Chantry committed
585
586
	int retval = 0;

587
588
589
#define HEAD 1
#define TAIL 0

Xavier Chantry's avatar
Xavier Chantry committed
590
	/* save the cwd so we can restore it later */
591
	OPEN(cwdfd, ".", O_RDONLY | O_CLOEXEC);
592
	if(cwdfd < 0) {
593
		_alpm_log(handle, ALPM_LOG_ERROR, _("could not get current working directory\n"));
Xavier Chantry's avatar
Xavier Chantry committed
594
595
596
	}

	/* just in case our cwd was removed in the upgrade operation */
Dan McGee's avatar
Dan McGee committed
597
	if(chdir(handle->root) != 0) {
598
		_alpm_log(handle, ALPM_LOG_ERROR, _("could not change directory to %s (%s)\n"),
Dan McGee's avatar
Dan McGee committed
599
				handle->root, strerror(errno));
Xavier Chantry's avatar
Xavier Chantry committed
600
601
602
		goto cleanup;
	}

603
	_alpm_log(handle, ALPM_LOG_DEBUG, "executing \"%s\" under chroot \"%s\"\n",
604
			cmd, handle->root);
Xavier Chantry's avatar
Xavier Chantry committed
605
606
607
608

	/* Flush open fds before fork() to avoid cloning buffers */
	fflush(NULL);

609
	if(socketpair(AF_UNIX, SOCK_STREAM, 0, child2parent_pipefd) == -1) {
610
611
612
613
614
		_alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
		retval = 1;
		goto cleanup;
	}

615
	if(socketpair(AF_UNIX, SOCK_STREAM, 0, parent2child_pipefd) == -1) {
616
		_alpm_log(handle, ALPM_LOG_ERROR, _("could not create pipe (%s)\n"), strerror(errno));
617
618
619
620
		retval = 1;
		goto cleanup;
	}

621
	/* fork- parent and child each have separate code blocks below */
Xavier Chantry's avatar
Xavier Chantry committed
622
623
	pid = fork();
	if(pid == -1) {
624
		_alpm_log(handle, ALPM_LOG_ERROR, _("could not fork a new process (%s)\n"), strerror(errno));
Xavier Chantry's avatar
Xavier Chantry committed
625
626
627
628
629
630
		retval = 1;
		goto cleanup;
	}

	if(pid == 0) {
		/* this code runs for the child only (the actual chroot/exec) */
631
		close(0);
632
633
		close(1);
		close(2);
634
635
636
637
638
639
640
		while(dup2(child2parent_pipefd[HEAD], 1) == -1 && errno == EINTR);
		while(dup2(child2parent_pipefd[HEAD], 2) == -1 && errno == EINTR);
		while(dup2(parent2child_pipefd[TAIL], 0) == -1 && errno == EINTR);
		close(parent2child_pipefd[TAIL]);
		close(parent2child_pipefd[HEAD]);
		close(child2parent_pipefd[TAIL]);
		close(child2parent_pipefd[HEAD]);
641
642
643
		if(cwdfd >= 0) {
			close(cwdfd);
		}
644
645

		/* use fprintf instead of _alpm_log to send output through the parent */
Dan McGee's avatar
Dan McGee committed
646
		if(chroot(handle->root) != 0) {
647
			fprintf(stderr, _("could not change the root directory (%s)\n"), strerror(errno));
Xavier Chantry's avatar
Xavier Chantry committed
648
649
650
			exit(1);
		}
		if(chdir("/") != 0) {
651
652
			fprintf(stderr, _("could not change directory to %s (%s)\n"),
					"/", strerror(errno));
Xavier Chantry's avatar
Xavier Chantry committed
653
654
655
			exit(1);
		}
		umask(0022);
656
		_alpm_reset_signals();
657
658
659
		execv(cmd, argv);
		/* execv only returns if there was an error */
		fprintf(stderr, _("call to execv failed (%s)\n"), strerror(errno));
660
		exit(1);
Xavier Chantry's avatar
Xavier Chantry committed
661
662
663
	} else {
		/* this code runs for the parent only (wait on the child) */
		int status;
664
665
666
667
668
		char obuf[PIPE_BUF]; /* writes <= PIPE_BUF are guaranteed atomic */
		char ibuf[LINE_MAX];
		ssize_t olen = 0, ilen = 0;
		nfds_t nfds = 2;
		struct pollfd fds[2], *child2parent = &(fds[0]), *parent2child = &(fds[1]);
669
		int poll_ret;
670

671
		child2parent->fd = child2parent_pipefd[TAIL];
672
673
		child2parent->events = POLLIN;
		fcntl(child2parent->fd, F_SETFL, O_NONBLOCK);
674
675
		close(child2parent_pipefd[HEAD]);
		close(parent2child_pipefd[TAIL]);
676
677

		if(stdin_cb) {
678
			parent2child->fd = parent2child_pipefd[HEAD];
679
680
			parent2child->events = POLLOUT;
			fcntl(parent2child->fd, F_SETFL, O_NONBLOCK);
Xavier Chantry's avatar
Xavier Chantry committed
681
		} else {
682
683
			parent2child->fd = -1;
			parent2child->events = 0;
684
			close(parent2child_pipefd[HEAD]);
685
686
687
688
689
		}

#define STOP_POLLING(p) do { close(p->fd); p->fd = -1; } while(0)

		while((child2parent->fd != -1 || parent2child->fd != -1)
690
691
692
693
694
695
696
697
				&& (poll_ret = poll(fds, nfds, -1)) != 0) {
			if(poll_ret == -1) {
				if(errno == EINTR) {
					continue;
				} else {
					break;
				}
			}
698
699
700
701
702
			if(child2parent->revents & POLLIN) {
				if(_alpm_chroot_read_from_child(handle, child2parent->fd,
							ibuf, &ilen, sizeof(ibuf)) != 0) {
					/* we encountered end-of-file or an error */
					STOP_POLLING(child2parent);
Andrew Gregory's avatar
Andrew Gregory committed
703
				}
704
705
706
			} else if(child2parent->revents) {
				/* anything but POLLIN indicates an error */
				STOP_POLLING(child2parent);
707
			}
708
709
710
711
712
713
714
715
716
717
			if(parent2child->revents & POLLOUT) {
				if(_alpm_chroot_write_to_child(handle, parent2child->fd, obuf, &olen,
							sizeof(obuf), stdin_cb, stdin_ctx) != 0) {
					STOP_POLLING(parent2child);
				}
			} else if(parent2child->revents) {
				/* anything but POLLOUT indicates an error */
				STOP_POLLING(parent2child);
			}
		}
718
719
720
721
722
723
		/* process anything left in the input buffer */
		if(ilen) {
			/* buffer would have already been flushed if it had a newline */
			strcpy(ibuf + ilen, "\n");
			_alpm_chroot_process_output(handle, ibuf);
		}
724
725

#undef STOP_POLLING
726
727
#undef HEAD
#undef TAIL
728
729
730
731
732
733

		if(parent2child->fd != -1) {
			close(parent2child->fd);
		}
		if(child2parent->fd != -1) {
			close(child2parent->fd);
734
735
736
737
		}

		while(waitpid(pid, &status, 0) == -1) {
			if(errno != EINTR) {
738
				_alpm_log(handle, ALPM_LOG_ERROR, _("call to waitpid failed (%s)\n"), strerror(errno));
739
740
741
742
743
744
745
				retval = 1;
				goto cleanup;
			}
		}

		/* check the return status, make sure it is 0 (success) */
		if(WIFEXITED(status)) {
746
			_alpm_log(handle, ALPM_LOG_DEBUG, "call to waitpid succeeded\n");
747
			if(WEXITSTATUS(status) != 0) {
748
				_alpm_log(handle, ALPM_LOG_ERROR, _("command failed to execute correctly\n"));
749
				retval = 1;
Xavier Chantry's avatar
Xavier Chantry committed
750
			}
751
752
753
754
755
756
757
758
759
		} else if(WIFSIGNALED(status) != 0) {
			char *signal_description = strsignal(WTERMSIG(status));
			/* strsignal can return NULL on some (non-Linux) platforms */
			if(signal_description == NULL) {
				signal_description = _("Unknown signal");
			}
			_alpm_log(handle, ALPM_LOG_ERROR, _("command terminated by signal %d: %s\n"),
						WTERMSIG(status), signal_description);
			retval = 1;
Xavier Chantry's avatar
Xavier Chantry committed
760
761
762
763
		}
	}

cleanup:
764
765
766
767
768
	if(cwdfd >= 0) {
		if(fchdir(cwdfd) != 0) {
			_alpm_log(handle, ALPM_LOG_ERROR,
					_("could not restore working directory (%s)\n"), strerror(errno));
		}
769
		close(cwdfd);
Xavier Chantry's avatar
Xavier Chantry committed
770
771
	}

772
	return retval;
Xavier Chantry's avatar
Xavier Chantry committed
773
774
}

775
776
777
778
/** Run ldconfig in a chroot.
 * @param handle the context handle
 * @return 0 on success, 1 on error
 */
779
int _alpm_ldconfig(alpm_handle_t *handle)
Judd Vinet's avatar
Judd Vinet committed
780
781
782
{
	char line[PATH_MAX];

783
	_alpm_log(handle, ALPM_LOG_DEBUG, "running ldconfig\n");
Xavier Chantry's avatar
Xavier Chantry committed
784

Dan McGee's avatar
Dan McGee committed
785
	snprintf(line, PATH_MAX, "%setc/ld.so.conf", handle->root);
786
	if(access(line, F_OK) == 0) {
787
		snprintf(line, PATH_MAX, "%s%s", handle->root, LDCONFIG);
788
789
790
791
		if(access(line, X_OK) == 0) {
			char arg0[32];
			char *argv[] = { arg0, NULL };
			strcpy(arg0, "ldconfig");
792
			return _alpm_run_chroot(handle, LDCONFIG, argv, NULL, NULL);
793
		}
Judd Vinet's avatar
Judd Vinet committed
794
795
	}

796
	return 0;
Judd Vinet's avatar
Judd Vinet committed
797
798
}

799
800
801
802
803
804
805
/** Helper function for comparing strings using the alpm "compare func"
 * signature.
 * @param s1 first string to be compared
 * @param s2 second string to be compared
 * @return 0 if strings are equal, positive int if first unequal character
 * has a greater value in s1, negative if it has a greater value in s2
 */
806
807
int _alpm_str_cmp(const void *s1, const void *s2)
{
808
	return strcmp(s1, s2);
809
810
}

811
/** Find a filename in a registered alpm cachedir.
812
 * @param handle the context handle
813
 * @param filename name of file to find
814
815
 * @return malloced path of file, NULL if not found
 */
816
char *_alpm_filecache_find(alpm_handle_t *handle, const char *filename)
817
818
819
820
{
	char path[PATH_MAX];
	char *retpath;
	alpm_list_t *i;
821
	struct stat buf;
822
823

	/* Loop through the cache dirs until we find a matching file */
824
	for(i = handle->cachedirs; i; i = i->next) {
825
		snprintf(path, PATH_MAX, "%s%s", (char *)i->data,
826
				filename);
827
		if(stat(path, &buf) == 0 && S_ISREG(buf.st_mode)) {
828
			retpath = strdup(path);
829
			_alpm_log(handle, ALPM_LOG_DEBUG, "found cached pkg: %s\n", retpath);
830
			return retpath;
831
832
833
		}
	}
	/* package wasn't found in any cachedir */
Dan McGee's avatar
Dan McGee committed
834
	return NULL;
835
836
}

837
/** Check the alpm cachedirs for existence and find a writable one.
838
 * If no valid cache directory can be found, use /tmp.
839
 * @param handle the context handle
840
841
 * @return pointer to a writable cache directory.
 */
842
const char *_alpm_filecache_setup(alpm_handle_t *handle)
843
844
{
	struct stat buf;
845
	alpm_list_t *i;
846
847
	char *cachedir;
	const char *tmpdir;
848

849
850
	/* Loop through the cache dirs until we find a usable directory */
	for(i = handle->cachedirs; i; i = i->next) {
851
		cachedir = i->data;
852
853
		if(stat(cachedir, &buf) != 0) {
			/* cache directory does not exist.... try creating it */
854
			_alpm_log(handle, ALPM_LOG_WARNING, _("no %s cache exists, creating...\n"),
855
856
					cachedir);
			if(_alpm_makepath(cachedir) == 0) {
857
				_alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
858
				return cachedir;
859
			}
860
861
862
		} else if(!S_ISDIR(buf.st_mode)) {
			_alpm_log(handle, ALPM_LOG_DEBUG,
					"skipping cachedir, not a directory: %s\n", cachedir);
863
		} else if(_alpm_access(handle, NULL, cachedir, W_OK) != 0) {
864
865
866
867
868
869
			_alpm_log(handle, ALPM_LOG_DEBUG,
					"skipping cachedir, not writable: %s\n", cachedir);
		} else if(!(buf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))) {
			_alpm_log(handle, ALPM_LOG_DEBUG,
					"skipping cachedir, no write bits set: %s\n", cachedir);
		} else {
870
			_alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
871
			return cachedir;
872
873
874
		}
	}

875
876
877
878
879
880
	/* we didn't find a valid cache directory. use TMPDIR or /tmp. */
	if((tmpdir = getenv("TMPDIR")) && stat(tmpdir, &buf) && S_ISDIR(buf.st_mode)) {
		/* TMPDIR was good, we can use it */
	} else {
		tmpdir = "/tmp";
	}
881
882
	alpm_option_add_cachedir(handle, tmpdir);
	cachedir = handle->cachedirs->prev->data;
883
884
885
886
	_alpm_log(handle, ALPM_LOG_DEBUG, "using cachedir: %s\n", cachedir);
	_alpm_log(handle, ALPM_LOG_WARNING,
			_("couldn't find or create package cache, using %s instead\n"), cachedir);
	return cachedir;
887
888
}

889
#if defined  HAVE_LIBSSL || defined HAVE_LIBNETTLE
890
891
892
893
894
/** Compute the MD5 message digest of a file.
 * @param path file path of file to compute  MD5 digest of
 * @param output string to hold computed MD5 digest
 * @return 0 on success, 1 on file open error, 2 on file read error
 */
895
896
static int md5_file(const char *path, unsigned char output[16])
{
897
#if HAVE_LIBSSL
Dan McGee's avatar
Dan McGee committed
898
	MD5_CTX ctx;
899
900
901
#else /* HAVE_LIBNETTLE */
	struct md5_ctx ctx;
#endif
Dan McGee's avatar
Dan McGee committed
902
	unsigned char *buf;
903
904
	ssize_t n;
	int fd;
905

906
	MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);
907

908
	OPEN(fd, path, O_RDONLY | O_CLOEXEC);
909
	if(fd < 0) {
Dan McGee's avatar
Dan McGee committed
910
		free(buf);
911
		return 1;
Dan McGee's avatar
Dan McGee committed
912
	}
913

914
#if HAVE_LIBSSL
Dan McGee's avatar
Dan McGee committed
915
	MD5_Init(&ctx);
916
917
918
#else /* HAVE_LIBNETTLE */
	md5_init(&ctx);
#endif
919

920
921
922
923
	while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
		if(n < 0) {
			continue;
		}
924
#if HAVE_LIBSSL
Dan McGee's avatar
Dan McGee committed
925
		MD5_Update(&ctx, buf, n);
926
927
928
#else /* HAVE_LIBNETTLE */
		md5_update(&ctx, n, buf);
#endif
Dan McGee's avatar
Dan McGee committed
929
	}
930

931
	close(fd);
Dan McGee's avatar
Dan McGee committed
932
	free(buf);
933

934
	if(n < 0) {
935
		return 2;
Dan McGee's avatar
Dan McGee committed
936
	}
937

938
#if HAVE_LIBSSL
939
	MD5_Final(output, &ctx);
940
941
942
#else /* HAVE_LIBNETTLE */
	md5_digest(&ctx, MD5_DIGEST_SIZE, output);
#endif
943
	return 0;
944
}
Dan McGee's avatar
Dan McGee committed
945

Allan McRae's avatar
Allan McRae committed
946
947
948
/** Compute the SHA-256 message digest of a file.
 * @param path file path of file to compute SHA256 digest of
 * @param output string to hold computed SHA256 digest
949
950
 * @return 0 on success, 1 on file open error, 2 on file read error
 */
Allan McRae's avatar
Allan McRae committed
951
static int sha256_file(const char *path, unsigned char output[32])
Dan McGee's avatar
Dan McGee committed
952
{
953
#if HAVE_LIBSSL
Dan McGee's avatar
Dan McGee committed
954
	SHA256_CTX ctx;
955
956
957
#else /* HAVE_LIBNETTLE */
	struct sha256_ctx ctx;
#endif
Dan McGee's avatar
Dan McGee committed
958
	unsigned char *buf;
959
960
	ssize_t n;
	int fd;
Dan McGee's avatar
Dan McGee committed
961

962
	MALLOC(buf, (size_t)ALPM_BUFFER_SIZE, return 1);
Dan McGee's avatar
Dan McGee committed
963

964
	OPEN(fd, path, O_RDONLY | O_CLOEXEC);
965
	if(fd < 0) {
Dan McGee's avatar
Dan McGee committed
966
967
968
969
		free(buf);
		return 1;
	}

970
#if HAVE_LIBSSL
Allan McRae's avatar
Allan McRae committed
971
	SHA256_Init(&ctx);
972
973
974
#else /* HAVE_LIBNETTLE */
	sha256_init(&ctx);
#endif
Dan McGee's avatar
Dan McGee committed
975

976
977
978
979
	while((n = read(fd, buf, ALPM_BUFFER_SIZE)) > 0 || errno == EINTR) {
		if(n < 0) {
			continue;
		}
980
#if HAVE_LIBSSL
Allan McRae's avatar
Allan McRae committed
981
		SHA256_Update(&ctx, buf, n);
982
983
984
#else /* HAVE_LIBNETTLE */
		sha256_update(&ctx, n, buf);
#endif
Dan McGee's avatar
Dan McGee committed
985
986
	}

987
	close(fd);
Dan McGee's avatar
Dan McGee committed
988
989
	free(buf);

990
	if(n < 0) {
Dan McGee's avatar
Dan McGee committed
991
992
993
		return 2;
	}