conf.c 31.6 KB
Newer Older
Judd Vinet's avatar
Judd Vinet committed
1
2
/*
 *  conf.c
Dan McGee's avatar
Dan McGee committed
3
 *
4
 *  Copyright (c) 2006-2020 Pacman Development Team <pacman-dev@archlinux.org>
5
 *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
Dan McGee's avatar
Dan McGee committed
6
 *
Judd Vinet's avatar
Judd Vinet committed
7
8
9
10
11
12
13
14
15
16
17
 *  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
18
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
Judd Vinet's avatar
Judd Vinet committed
19
20
 */

Dan McGee's avatar
Dan McGee committed
21
22
#include <errno.h>
#include <limits.h>
23
#include <locale.h> /* setlocale */
24
#include <fcntl.h> /* open */
25
#include <glob.h>
Judd Vinet's avatar
Judd Vinet committed
26
27
#include <stdlib.h>
#include <stdio.h>
28
#include <string.h> /* strdup */
Dan McGee's avatar
Dan McGee committed
29
#include <sys/stat.h>
30
#include <sys/types.h>
Dan McGee's avatar
Dan McGee committed
31
#include <sys/utsname.h> /* uname */
Andrew Gregory's avatar
Andrew Gregory committed
32
#include <sys/wait.h>
Dan McGee's avatar
Dan McGee committed
33
#include <unistd.h>
Judd Vinet's avatar
Judd Vinet committed
34
35

/* pacman */
36
#include "conf.h"
37
#include "ini.h"
38
#include "util.h"
39
#include "callback.h"
Judd Vinet's avatar
Judd Vinet committed
40

Dan McGee's avatar
Dan McGee committed
41
42
43
/* global config variable */
config_t *config = NULL;

44
45
#define NOCOLOR       "\033[0m"

46
47
#define BOLD          "\033[0;1m"

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#define BLACK         "\033[0;30m"
#define RED           "\033[0;31m"
#define GREEN         "\033[0;32m"
#define YELLOW        "\033[0;33m"
#define BLUE          "\033[0;34m"
#define MAGENTA       "\033[0;35m"
#define CYAN          "\033[0;36m"
#define WHITE         "\033[0;37m"

#define BOLDBLACK     "\033[1;30m"
#define BOLDRED       "\033[1;31m"
#define BOLDGREEN     "\033[1;32m"
#define BOLDYELLOW    "\033[1;33m"
#define BOLDBLUE      "\033[1;34m"
#define BOLDMAGENTA   "\033[1;35m"
#define BOLDCYAN      "\033[1;36m"
#define BOLDWHITE     "\033[1;37m"
65
#define GREY46        "\033[38;5;243m"
66
67
68
69
70
71

void enable_colors(int colors)
{
	colstr_t *colstr = &config->colstr;

	if(colors == PM_COLOR_ON) {
72
73
		colstr->colon   = BOLDBLUE "::" BOLD " ";
		colstr->title   = BOLD;
74
75
76
77
78
79
		colstr->repo    = BOLDMAGENTA;
		colstr->version = BOLDGREEN;
		colstr->groups  = BOLDBLUE;
		colstr->meta    = BOLDCYAN;
		colstr->warn    = BOLDYELLOW;
		colstr->err     = BOLDRED;
80
		colstr->faint   = GREY46;
81
		colstr->nocolor = NOCOLOR;
82
83
84
85
86
87
88
89
90
	} else {
		colstr->colon   = ":: ";
		colstr->title   = "";
		colstr->repo    = "";
		colstr->version = "";
		colstr->groups  = "";
		colstr->meta    = "";
		colstr->warn    = "";
		colstr->err     = "";
91
		colstr->faint   = "";
92
		colstr->nocolor = "";
93
94
95
	}
}

96
config_t *config_new(void)
97
{
98
99
	config_t *newconfig = calloc(1, sizeof(config_t));
	if(!newconfig) {
100
		pm_printf(ALPM_LOG_ERROR,
101
102
				_n("malloc failure: could not allocate %zu byte\n",
				   "malloc failure: could not allocate %zu bytes\n", sizeof(config_t)),
Allan McRae's avatar
Allan McRae committed
103
				sizeof(config_t));
104
		return NULL;
Dan McGee's avatar
Dan McGee committed
105
	}
106
	/* defaults which may get overridden later */
107
	newconfig->op = PM_OP_MAIN;
108
	newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING;
109
	newconfig->configfile = strdup(CONFFILE);
110
111
112
	if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) {
		newconfig->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL |
			ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL;
113
114
		newconfig->localfilesiglevel = ALPM_SIG_USE_DEFAULT;
		newconfig->remotefilesiglevel = ALPM_SIG_USE_DEFAULT;
115
	}
116

117
118
119
120
121
122
123
124
	newconfig->colstr.colon   = ":: ";
	newconfig->colstr.title   = "";
	newconfig->colstr.repo    = "";
	newconfig->colstr.version = "";
	newconfig->colstr.groups  = "";
	newconfig->colstr.meta    = "";
	newconfig->colstr.warn    = "";
	newconfig->colstr.err     = "";
125
	newconfig->colstr.faint   = "";
126
127
	newconfig->colstr.nocolor = "";

128
	return newconfig;
129
130
}

131
int config_free(config_t *oldconfig)
132
{
133
	if(oldconfig == NULL) {
134
		return -1;
135
136
	}

137
138
139
	alpm_list_free(oldconfig->explicit_adds);
	alpm_list_free(oldconfig->explicit_removes);

140
141
142
	alpm_list_free_inner(config->repos, (alpm_list_fn_free) config_repo_free);
	alpm_list_free(config->repos);

Nagy Gabor's avatar
Nagy Gabor committed
143
	FREELIST(oldconfig->holdpkg);
144
145
	FREELIST(oldconfig->ignorepkg);
	FREELIST(oldconfig->ignoregrp);
Florian Pritz's avatar
Florian Pritz committed
146
	FREELIST(oldconfig->assumeinstalled);
147
148
	FREELIST(oldconfig->noupgrade);
	FREELIST(oldconfig->noextract);
149
	FREELIST(oldconfig->overwrite_files);
150
	free(oldconfig->configfile);
151
152
153
	free(oldconfig->rootdir);
	free(oldconfig->dbpath);
	free(oldconfig->logfile);
154
	free(oldconfig->gpgdir);
155
	FREELIST(oldconfig->hookdirs);
156
	FREELIST(oldconfig->cachedirs);
157
	free(oldconfig->xfercommand);
158
	free(oldconfig->print_format);
159
	free(oldconfig->arch);
Andrew Gregory's avatar
Andrew Gregory committed
160
	wordsplit_free(oldconfig->xfercommand_argv);
161
	free(oldconfig);
162

163
	return 0;
164
165
}

166
167
168
169
170
void config_repo_free(config_repo_t *repo)
{
	if(repo == NULL) {
		return;
	}
171
	free(repo->name);
172
173
174
175
	FREELIST(repo->servers);
	free(repo);
}

Dan McGee's avatar
Dan McGee committed
176
/** Helper function for download_with_xfercommand() */
Gerardo Exequiel Pozzi's avatar
Gerardo Exequiel Pozzi committed
177
178
static char *get_filename(const char *url)
{
Dan McGee's avatar
Dan McGee committed
179
180
181
182
183
184
185
186
	char *filename = strrchr(url, '/');
	if(filename != NULL) {
		filename++;
	}
	return filename;
}

/** Helper function for download_with_xfercommand() */
Gerardo Exequiel Pozzi's avatar
Gerardo Exequiel Pozzi committed
187
188
static char *get_destfile(const char *path, const char *filename)
{
Dan McGee's avatar
Dan McGee committed
189
190
191
192
193
194
195
196
197
198
	char *destfile;
	/* len = localpath len + filename len + null */
	size_t len = strlen(path) + strlen(filename) + 1;
	destfile = calloc(len, sizeof(char));
	snprintf(destfile, len, "%s%s", path, filename);

	return destfile;
}

/** Helper function for download_with_xfercommand() */
Gerardo Exequiel Pozzi's avatar
Gerardo Exequiel Pozzi committed
199
200
static char *get_tempfile(const char *path, const char *filename)
{
Dan McGee's avatar
Dan McGee committed
201
202
203
204
205
206
207
208
209
	char *tempfile;
	/* len = localpath len + filename len + '.part' len + null */
	size_t len = strlen(path) + strlen(filename) + 6;
	tempfile = calloc(len, sizeof(char));
	snprintf(tempfile, len, "%s%s.part", path, filename);

	return tempfile;
}

Andrew Gregory's avatar
Andrew Gregory committed
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
/* system()/exec() hybrid function allowing exec()-style direct execution
 * of a command with the simplicity of system()
 * - not thread-safe
 * - errno may be set by fork(), pipe(), or execvp()
 */
static int systemvp(const char *file, char *const argv[])
{
	int pid, err = 0, ret = -1, err_fd[2];
	sigset_t oldblock;
	struct sigaction sa_ign = { .sa_handler = SIG_IGN }, oldint, oldquit;

	if(pipe(err_fd) != 0) {
		return -1;
	}

	sigaction(SIGINT, &sa_ign, &oldint);
	sigaction(SIGQUIT, &sa_ign, &oldquit);
	sigaddset(&sa_ign.sa_mask, SIGCHLD);
	sigprocmask(SIG_BLOCK, &sa_ign.sa_mask, &oldblock);

	pid = fork();

	/* child */
	if(pid == 0) {
		close(err_fd[0]);
		fcntl(err_fd[1], F_SETFD, FD_CLOEXEC);

		/* restore signal handling for the child to inherit */
		sigaction(SIGINT, &oldint, NULL);
		sigaction(SIGQUIT, &oldquit, NULL);
		sigprocmask(SIG_SETMASK, &oldblock, NULL);

		execvp(file, argv);

		/* execvp failed, pass the error back to the parent */
		while(write(err_fd[1], &errno, sizeof(errno)) == -1 && errno == EINTR);
		_Exit(127);
	}

	/* parent */
	close(err_fd[1]);

	if(pid != -1)  {
		int wret;
		while((wret = waitpid(pid, &ret, 0)) == -1 && errno == EINTR);
		if(wret > 0) {
			while(read(err_fd[0], &err, sizeof(err)) == -1 && errno == EINTR);
		}
	} else {
		/* fork failed, make sure errno is preserved after cleanup */
		err = errno;
	}

	close(err_fd[0]);

	sigaction(SIGINT, &oldint, NULL);
	sigaction(SIGQUIT, &oldquit, NULL);
	sigprocmask(SIG_SETMASK, &oldblock, NULL);

	if(err) {
		errno = err;
		ret = -1;
	}

	return ret;
}

Dan McGee's avatar
Dan McGee committed
277
278
/** External fetch callback */
static int download_with_xfercommand(const char *url, const char *localpath,
Gerardo Exequiel Pozzi's avatar
Gerardo Exequiel Pozzi committed
279
280
		int force)
{
281
	int ret = 0, retval;
Dan McGee's avatar
Dan McGee committed
282
	int usepart = 0;
Andrew Gregory's avatar
Andrew Gregory committed
283
	int cwdfd = -1;
Dan McGee's avatar
Dan McGee committed
284
285
	struct stat st;
	char *destfile, *tempfile, *filename;
Andrew Gregory's avatar
Andrew Gregory committed
286
287
	const char **argv;
	size_t i;
Dan McGee's avatar
Dan McGee committed
288

Andrew Gregory's avatar
Andrew Gregory committed
289
	if(!config->xfercommand_argv) {
Dan McGee's avatar
Dan McGee committed
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
		return -1;
	}

	filename = get_filename(url);
	if(!filename) {
		return -1;
	}
	destfile = get_destfile(localpath, filename);
	tempfile = get_tempfile(localpath, filename);

	if(force && stat(tempfile, &st) == 0) {
		unlink(tempfile);
	}
	if(force && stat(destfile, &st) == 0) {
		unlink(destfile);
	}

Andrew Gregory's avatar
Andrew Gregory committed
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
	if((argv = calloc(config->xfercommand_argc + 1, sizeof(char*))) == NULL) {
		size_t bytes = (config->xfercommand_argc + 1) * sizeof(char*);
		pm_printf(ALPM_LOG_ERROR,
				_n("malloc failure: could not allocate %zu byte\n",
				   "malloc failure: could not allocate %zu bytes\n",
					 bytes),
				bytes);
		goto cleanup;
	}

	for(i = 0; i <= config->xfercommand_argc; i++) {
		const char *val = config->xfercommand_argv[i];
		if(val && strcmp(val, "%o") == 0) {
			usepart = 1;
			val = tempfile;
		} else if(val && strcmp(val, "%u") == 0) {
			val = url;
		}
		argv[i] = val;
Dan McGee's avatar
Dan McGee committed
326
327
328
	}

	/* save the cwd so we can restore it later */
329
330
331
332
	do {
		cwdfd = open(".", O_RDONLY);
	} while(cwdfd == -1 && errno == EINTR);
	if(cwdfd < 0) {
333
		pm_printf(ALPM_LOG_ERROR, _("could not get current working directory\n"));
Dan McGee's avatar
Dan McGee committed
334
335
336
337
	}

	/* cwd to the download directory */
	if(chdir(localpath)) {
338
		pm_printf(ALPM_LOG_WARNING, _("could not chdir to download directory %s\n"), localpath);
Dan McGee's avatar
Dan McGee committed
339
340
341
		ret = -1;
		goto cleanup;
	}
Andrew Gregory's avatar
Andrew Gregory committed
342
343
344
345
346
347
348
349
350

	if(config->logmask & ALPM_LOG_DEBUG) {
		char *cmd = arg_to_string(config->xfercommand_argc, (char**)argv);
		if(cmd) {
			pm_printf(ALPM_LOG_DEBUG, "running command: %s\n", cmd);
			free(cmd);
		}
	}
	retval = systemvp(argv[0], (char**)argv);
Dan McGee's avatar
Dan McGee committed
351
352

	if(retval == -1) {
353
		pm_printf(ALPM_LOG_WARNING, _("running XferCommand: fork failed!\n"));
Dan McGee's avatar
Dan McGee committed
354
355
356
		ret = -1;
	} else if(retval != 0) {
		/* download failed */
357
		pm_printf(ALPM_LOG_DEBUG, "XferCommand command returned non-zero status "
Dan McGee's avatar
Dan McGee committed
358
359
360
361
				"code (%d)\n", retval);
		ret = -1;
	} else {
		/* download was successful */
362
		ret = 0;
Dan McGee's avatar
Dan McGee committed
363
		if(usepart) {
364
365
366
367
368
			if(rename(tempfile, destfile)) {
				pm_printf(ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"),
						tempfile, destfile, strerror(errno));
				ret = -1;
			}
Dan McGee's avatar
Dan McGee committed
369
370
371
372
373
		}
	}

cleanup:
	/* restore the old cwd if we have it */
374
375
376
377
378
	if(cwdfd >= 0) {
		if(fchdir(cwdfd) != 0) {
			pm_printf(ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"),
					strerror(errno));
		}
379
		close(cwdfd);
Dan McGee's avatar
Dan McGee committed
380
381
382
383
384
385
386
387
	}

	if(ret == -1) {
		/* hack to let an user the time to cancel a download */
		sleep(2);
	}
	free(destfile);
	free(tempfile);
Andrew Gregory's avatar
Andrew Gregory committed
388
	free(argv);
Dan McGee's avatar
Dan McGee committed
389
390
391
392
393
394
395
396
397
398

	return ret;
}


int config_set_arch(const char *arch)
{
	if(strcmp(arch, "auto") == 0) {
		struct utsname un;
		uname(&un);
399
		config->arch = strdup(un.machine);
Dan McGee's avatar
Dan McGee committed
400
	} else {
401
		config->arch = strdup(arch);
Dan McGee's avatar
Dan McGee committed
402
	}
403
	pm_printf(ALPM_LOG_DEBUG, "config: arch: %s\n", config->arch);
404
	return 0;
Dan McGee's avatar
Dan McGee committed
405
406
}

407
408
409
410
411
/**
 * Parse a signature verification level line.
 * @param values the list of parsed option values
 * @param storage location to store the derived signature level; any existing
 * value here is used as a starting point
412
413
 * @param file path to the config file
 * @param linenum current line number in file
414
415
 * @return 0 on success, 1 on any parsing error
 */
416
417
static int process_siglevel(alpm_list_t *values, int *storage,
		int *storage_mask, const char *file, int linenum)
418
{
419
	int level = *storage, mask = *storage_mask;
420
421
422
	alpm_list_t *i;
	int ret = 0;

423
424
425
#define SLSET(sl) do { level |= (sl); mask |= (sl); } while(0)
#define SLUNSET(sl) do { level &= ~(sl); mask |= (sl); } while(0)

426
427
428
429
430
	/* Collapse the option names into a single bitmasked value */
	for(i = values; i; i = alpm_list_next(i)) {
		const char *original = i->data, *value;
		int package = 0, database = 0;

Dave Reisner's avatar
Dave Reisner committed
431
		if(strncmp(original, "Package", strlen("Package")) == 0) {
432
433
434
			/* only packages are affected, don't flip flags for databases */
			value = original + strlen("Package");
			package = 1;
Dave Reisner's avatar
Dave Reisner committed
435
		} else if(strncmp(original, "Database", strlen("Database")) == 0) {
436
437
438
439
440
441
442
443
444
445
446
447
			/* only databases are affected, don't flip flags for packages */
			value = original + strlen("Database");
			database = 1;
		} else {
			/* no prefix, so anything found will affect both packages and dbs */
			value = original;
			package = database = 1;
		}

		/* now parse out and store actual flag if it is valid */
		if(strcmp(value, "Never") == 0) {
			if(package) {
448
				SLUNSET(ALPM_SIG_PACKAGE);
449
450
			}
			if(database) {
451
				SLUNSET(ALPM_SIG_DATABASE);
452
453
454
			}
		} else if(strcmp(value, "Optional") == 0) {
			if(package) {
455
				SLSET(ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL);
456
457
			}
			if(database) {
458
				SLSET(ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL);
459
460
461
			}
		} else if(strcmp(value, "Required") == 0) {
			if(package) {
462
463
				SLSET(ALPM_SIG_PACKAGE);
				SLUNSET(ALPM_SIG_PACKAGE_OPTIONAL);
464
465
			}
			if(database) {
466
467
				SLSET(ALPM_SIG_DATABASE);
				SLUNSET(ALPM_SIG_DATABASE_OPTIONAL);
468
469
470
			}
		} else if(strcmp(value, "TrustedOnly") == 0) {
			if(package) {
471
				SLUNSET(ALPM_SIG_PACKAGE_MARGINAL_OK | ALPM_SIG_PACKAGE_UNKNOWN_OK);
472
473
			}
			if(database) {
474
				SLUNSET(ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK);
475
476
477
			}
		} else if(strcmp(value, "TrustAll") == 0) {
			if(package) {
478
				SLSET(ALPM_SIG_PACKAGE_MARGINAL_OK | ALPM_SIG_PACKAGE_UNKNOWN_OK);
479
480
			}
			if(database) {
481
				SLSET(ALPM_SIG_DATABASE_MARGINAL_OK | ALPM_SIG_DATABASE_UNKNOWN_OK);
482
483
			}
		} else {
484
485
486
			pm_printf(ALPM_LOG_ERROR,
					_("config file %s, line %d: invalid value for '%s' : '%s'\n"),
					file, linenum, "SigLevel", original);
487
488
489
			ret = 1;
		}
		level &= ~ALPM_SIG_USE_DEFAULT;
490
	}
491

492
493
494
#undef SLSET
#undef SLUNSET

495
496
497
	/* ensure we have sig checking ability and are actually turning it on */
	if(!(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) &&
			level & (ALPM_SIG_PACKAGE | ALPM_SIG_DATABASE)) {
498
499
500
		pm_printf(ALPM_LOG_ERROR,
				_("config file %s, line %d: '%s' option invalid, no signature support\n"),
				file, linenum, "SigLevel");
501
502
503
		ret = 1;
	}

504
505
	if(!ret) {
		*storage = level;
506
		*storage_mask = mask;
507
508
	}
	return ret;
509
}
Dan McGee's avatar
Dan McGee committed
510

511
/**
512
 * Merge the package entries of two signature verification levels.
513
 * @param base initial siglevel
514
 * @param over overriding siglevel
515
 * @return merged siglevel
516
 */
517
static int merge_siglevel(int base, int over, int mask)
518
{
519
	return mask ? (over & mask) | (base & ~mask) : over;
520
521
}

522
523
524
static int process_cleanmethods(alpm_list_t *values,
		const char *file, int linenum)
{
525
526
527
528
529
530
531
532
	alpm_list_t *i;
	for(i = values; i; i = alpm_list_next(i)) {
		const char *value = i->data;
		if(strcmp(value, "KeepInstalled") == 0) {
			config->cleanmethod |= PM_CLEAN_KEEPINST;
		} else if(strcmp(value, "KeepCurrent") == 0) {
			config->cleanmethod |= PM_CLEAN_KEEPCUR;
		} else {
533
534
535
			pm_printf(ALPM_LOG_ERROR,
					_("config file %s, line %d: invalid value for '%s' : '%s'\n"),
					file, linenum, "CleanMethod", value);
536
537
			return 1;
		}
Dan McGee's avatar
Dan McGee committed
538
539
540
541
542
543
544
545
546
	}
	return 0;
}

/** Add repeating options such as NoExtract, NoUpgrade, etc to libalpm
 * settings. Refactored out of the parseconfig code since all of them did
 * the exact same thing and duplicated code.
 * @param ptr a pointer to the start of the multiple options
 * @param option the string (friendly) name of the option, used for messages
547
 * @param list the list to add the option to
Dan McGee's avatar
Dan McGee committed
548
549
 */
static void setrepeatingoption(char *ptr, const char *option,
550
		alpm_list_t **list)
Dan McGee's avatar
Dan McGee committed
551
{
552
	char *val, *saveptr = NULL;
553
554
555
556
557
558

	val = strtok_r(ptr, " ", &saveptr);
	while(val) {
		*list = alpm_list_add(*list, strdup(val));
		pm_printf(ALPM_LOG_DEBUG, "config: %s: %s\n", option, val);
		val = strtok_r(NULL, " ", &saveptr);
Dan McGee's avatar
Dan McGee committed
559
560
561
562
563
564
565
566
567
	}
}

static int _parse_options(const char *key, char *value,
		const char *file, int linenum)
{
	if(value == NULL) {
		/* options without settings */
		if(strcmp(key, "UseSyslog") == 0) {
568
			config->usesyslog = 1;
569
			pm_printf(ALPM_LOG_DEBUG, "config: usesyslog\n");
Dan McGee's avatar
Dan McGee committed
570
571
		} else if(strcmp(key, "ILoveCandy") == 0) {
			config->chomp = 1;
572
			pm_printf(ALPM_LOG_DEBUG, "config: chomp\n");
Dan McGee's avatar
Dan McGee committed
573
574
		} else if(strcmp(key, "VerbosePkgLists") == 0) {
			config->verbosepkglists = 1;
575
			pm_printf(ALPM_LOG_DEBUG, "config: verbosepkglists\n");
Dan McGee's avatar
Dan McGee committed
576
577
		} else if(strcmp(key, "TotalDownload") == 0) {
			config->totaldownload = 1;
578
			pm_printf(ALPM_LOG_DEBUG, "config: totaldownload\n");
Dan McGee's avatar
Dan McGee committed
579
		} else if(strcmp(key, "CheckSpace") == 0) {
580
			config->checkspace = 1;
581
582
583
		} else if(strcmp(key, "Color") == 0) {
			if(config->color == PM_COLOR_UNSET) {
				config->color = isatty(fileno(stdout)) ? PM_COLOR_ON : PM_COLOR_OFF;
584
				enable_colors(config->color);
585
			}
586
587
		} else if(strcmp(key, "DisableDownloadTimeout") == 0) {
			config->disable_dl_timeout = 1;
Dan McGee's avatar
Dan McGee committed
588
		} else {
589
			pm_printf(ALPM_LOG_WARNING,
Dan McGee's avatar
Dan McGee committed
590
591
592
593
594
595
					_("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
					file, linenum, key, "options");
		}
	} else {
		/* options with settings */
		if(strcmp(key, "NoUpgrade") == 0) {
596
			setrepeatingoption(value, "NoUpgrade", &(config->noupgrade));
Dan McGee's avatar
Dan McGee committed
597
		} else if(strcmp(key, "NoExtract") == 0) {
598
			setrepeatingoption(value, "NoExtract", &(config->noextract));
Dan McGee's avatar
Dan McGee committed
599
		} else if(strcmp(key, "IgnorePkg") == 0) {
600
			setrepeatingoption(value, "IgnorePkg", &(config->ignorepkg));
Dan McGee's avatar
Dan McGee committed
601
		} else if(strcmp(key, "IgnoreGroup") == 0) {
602
			setrepeatingoption(value, "IgnoreGroup", &(config->ignoregrp));
Dan McGee's avatar
Dan McGee committed
603
		} else if(strcmp(key, "HoldPkg") == 0) {
604
605
606
			setrepeatingoption(value, "HoldPkg", &(config->holdpkg));
		} else if(strcmp(key, "CacheDir") == 0) {
			setrepeatingoption(value, "CacheDir", &(config->cachedirs));
607
608
		} else if(strcmp(key, "HookDir") == 0) {
			setrepeatingoption(value, "HookDir", &(config->hookdirs));
Dan McGee's avatar
Dan McGee committed
609
		} else if(strcmp(key, "Architecture") == 0) {
610
			if(!config->arch) {
Dan McGee's avatar
Dan McGee committed
611
612
613
614
615
616
				config_set_arch(value);
			}
		} else if(strcmp(key, "DBPath") == 0) {
			/* don't overwrite a path specified on the command line */
			if(!config->dbpath) {
				config->dbpath = strdup(value);
617
				pm_printf(ALPM_LOG_DEBUG, "config: dbpath: %s\n", value);
Dan McGee's avatar
Dan McGee committed
618
619
620
621
622
			}
		} else if(strcmp(key, "RootDir") == 0) {
			/* don't overwrite a path specified on the command line */
			if(!config->rootdir) {
				config->rootdir = strdup(value);
623
				pm_printf(ALPM_LOG_DEBUG, "config: rootdir: %s\n", value);
Dan McGee's avatar
Dan McGee committed
624
625
626
627
			}
		} else if(strcmp(key, "GPGDir") == 0) {
			if(!config->gpgdir) {
				config->gpgdir = strdup(value);
628
				pm_printf(ALPM_LOG_DEBUG, "config: gpgdir: %s\n", value);
Dan McGee's avatar
Dan McGee committed
629
630
631
632
			}
		} else if(strcmp(key, "LogFile") == 0) {
			if(!config->logfile) {
				config->logfile = strdup(value);
633
				pm_printf(ALPM_LOG_DEBUG, "config: logfile: %s\n", value);
Dan McGee's avatar
Dan McGee committed
634
635
			}
		} else if(strcmp(key, "XferCommand") == 0) {
Andrew Gregory's avatar
Andrew Gregory committed
636
637
638
639
640
641
642
643
644
645
646
			char **c;
			if((config->xfercommand_argv = wordsplit(value)) == NULL) {
				pm_printf(ALPM_LOG_WARNING,
						_("config file %s, line %d: invalid value for '%s' : '%s'\n"),
						file, linenum, "XferCommand", value);
				return 1;
			}
			config->xfercommand_argc = 0;
			for(c = config->xfercommand_argv; *c; c++) {
				config->xfercommand_argc++;
			}
Dan McGee's avatar
Dan McGee committed
647
			config->xfercommand = strdup(value);
648
			pm_printf(ALPM_LOG_DEBUG, "config: xfercommand: %s\n", value);
Dan McGee's avatar
Dan McGee committed
649
		} else if(strcmp(key, "CleanMethod") == 0) {
650
651
			alpm_list_t *methods = NULL;
			setrepeatingoption(value, "CleanMethod", &methods);
652
			if(process_cleanmethods(methods, file, linenum)) {
653
654
655
656
				FREELIST(methods);
				return 1;
			}
			FREELIST(methods);
657
658
659
		} else if(strcmp(key, "SigLevel") == 0) {
			alpm_list_t *values = NULL;
			setrepeatingoption(value, "SigLevel", &values);
660
661
			if(process_siglevel(values, &config->siglevel,
						&config->siglevel_mask, file, linenum)) {
662
				FREELIST(values);
Dan McGee's avatar
Dan McGee committed
663
664
				return 1;
			}
665
			FREELIST(values);
666
667
668
		} else if(strcmp(key, "LocalFileSigLevel") == 0) {
			alpm_list_t *values = NULL;
			setrepeatingoption(value, "LocalFileSigLevel", &values);
669
670
			if(process_siglevel(values, &config->localfilesiglevel,
						&config->localfilesiglevel_mask, file, linenum)) {
671
672
673
674
675
676
677
				FREELIST(values);
				return 1;
			}
			FREELIST(values);
		} else if(strcmp(key, "RemoteFileSigLevel") == 0) {
			alpm_list_t *values = NULL;
			setrepeatingoption(value, "RemoteFileSigLevel", &values);
678
679
			if(process_siglevel(values, &config->remotefilesiglevel,
						&config->remotefilesiglevel_mask, file, linenum)) {
680
681
682
683
				FREELIST(values);
				return 1;
			}
			FREELIST(values);
Dan McGee's avatar
Dan McGee committed
684
		} else {
685
			pm_printf(ALPM_LOG_WARNING,
Dan McGee's avatar
Dan McGee committed
686
687
688
689
690
691
692
693
					_("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
					file, linenum, key, "options");
		}

	}
	return 0;
}

694
static char *replace_server_vars(config_t *c, config_repo_t *r, const char *s)
Dan McGee's avatar
Dan McGee committed
695
{
696
697
698
699
700
701
702
703
704
705
706
707
708
709
	if(c->arch == NULL && strstr(s, "$arch")) {
		pm_printf(ALPM_LOG_ERROR,
				_("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"),
				s, "$arch", "Architecture");
		return NULL;
	}

	if(c->arch) {
		char *temp, *replaced;

		replaced = strreplace(s, "$arch", c->arch);

		temp = replaced;
		replaced = strreplace(temp, "$repo", r->name);
Dan McGee's avatar
Dan McGee committed
710
		free(temp);
711
712

		return replaced;
Dan McGee's avatar
Dan McGee committed
713
	} else {
714
		return strreplace(s, "$repo", r->name);
Dan McGee's avatar
Dan McGee committed
715
	}
716
}
Dan McGee's avatar
Dan McGee committed
717

718
719
720
static int _add_mirror(alpm_db_t *db, char *value)
{
	if(alpm_db_add_server(db, value) != 0) {
Dan McGee's avatar
Dan McGee committed
721
		/* pm_errno is set by alpm_db_setserver */
722
		pm_printf(ALPM_LOG_ERROR, _("could not add server URL to database '%s': %s (%s)\n"),
723
				alpm_db_get_name(db), value, alpm_strerror(alpm_errno(config->handle)));
Dan McGee's avatar
Dan McGee committed
724
725
726
727
728
729
		return 1;
	}

	return 0;
}

730
731
732
733
734
735
736
737
738
739
740
741
static int register_repo(config_repo_t *repo)
{
	alpm_list_t *i;
	alpm_db_t *db;

	db = alpm_register_syncdb(config->handle, repo->name, repo->siglevel);
	if(db == NULL) {
		pm_printf(ALPM_LOG_ERROR, _("could not register '%s' database (%s)\n"),
				repo->name, alpm_strerror(alpm_errno(config->handle)));
		return 1;
	}

742
743
744
	pm_printf(ALPM_LOG_DEBUG, "setting usage of %d for %s repository\n",
			repo->usage, repo->name);
	alpm_db_set_usage(db, repo->usage);
745
746

	for(i = repo->servers; i; i = alpm_list_next(i)) {
747
		if(_add_mirror(db, i->data) != 0) {
748
749
750
751
752
753
754
			return 1;
		}
	}

	return 0;
}

755
/** Sets up libalpm global stuff in one go. Called after the command line
756
 * and initial config file parsing. Once this is complete, we can see if any
Dan McGee's avatar
Dan McGee committed
757
758
759
760
 * paths were defined. If a rootdir was defined and nothing else, we want all
 * of our paths to live under the rootdir that was specified. Safe to call
 * multiple times (will only do anything the first time).
 */
761
static int setup_libalpm(void)
Dan McGee's avatar
Dan McGee committed
762
{
763
	int ret = 0;
764
	alpm_errno_t err;
765
	alpm_handle_t *handle;
Florian Pritz's avatar
Florian Pritz committed
766
	alpm_list_t *i;
767

768
	pm_printf(ALPM_LOG_DEBUG, "setup_libalpm called\n");
769

770
	/* initialize library */
771
772
	handle = alpm_initialize(config->rootdir, config->dbpath, &err);
	if(!handle) {
773
774
		pm_printf(ALPM_LOG_ERROR, _("failed to initialize alpm library:\n(root: %s, dbpath: %s)\n%s\n"),
		        config->rootdir, config->dbpath, alpm_strerror(err));
775
		if(err == ALPM_ERR_DB_VERSION) {
Allan McRae's avatar
Allan McRae committed
776
			fprintf(stderr, _("try running pacman-db-upgrade\n"));
777
		}
778
779
		return -1;
	}
780
	config->handle = handle;
781

782
	alpm_option_set_logcb(handle, cb_log);
783
	alpm_option_set_dlcb(handle, cb_dl_progress);
784
785
786
	alpm_option_set_eventcb(handle, cb_event);
	alpm_option_set_questioncb(handle, cb_question);
	alpm_option_set_progresscb(handle, cb_progress);
787

788
789
790
791
	if(config->op == PM_OP_FILES) {
		alpm_option_set_dbext(handle, ".files");
	}

792
	ret = alpm_option_set_logfile(handle, config->logfile);
793
	if(ret != 0) {
794
		pm_printf(ALPM_LOG_ERROR, _("problem setting logfile '%s' (%s)\n"),
795
				config->logfile, alpm_strerror(alpm_errno(handle)));
796
		return ret;
797
	}
Dan McGee's avatar
Dan McGee committed
798

799
	/* Set GnuPG's home directory. This is not relative to rootdir, even if
800
	 * rootdir is defined. Reasoning: gpgdir contains configuration data. */
801
	ret = alpm_option_set_gpgdir(handle, config->gpgdir);
802
	if(ret != 0) {
803
		pm_printf(ALPM_LOG_ERROR, _("problem setting gpgdir '%s' (%s)\n"),
804
				config->gpgdir, alpm_strerror(alpm_errno(handle)));
805
		return ret;
Dan McGee's avatar
Dan McGee committed
806
	}
807

808
809
	/* Set user hook directory. This is not relative to rootdir, even if
	 * rootdir is defined. Reasoning: hookdir contains configuration data. */
810
811
812
	/* add hook directories 1-by-1 to avoid overwriting the system directory */
	for(i = config->hookdirs; i; i = alpm_list_next(i)) {
		if((ret = alpm_option_add_hookdir(handle, i->data)) != 0) {
813
			pm_printf(ALPM_LOG_ERROR, _("problem adding hookdir '%s' (%s)\n"),
814
					(char *) i->data, alpm_strerror(alpm_errno(handle)));
815
816
817
818
			return ret;
		}
	}

819
	alpm_option_set_cachedirs(handle, config->cachedirs);
820

821
822
	alpm_option_set_overwrite_files(handle, config->overwrite_files);

823
	alpm_option_set_default_siglevel(handle, config->siglevel);
824

825
826
827
	alpm_option_set_local_file_siglevel(handle, config->localfilesiglevel);
	alpm_option_set_remote_file_siglevel(handle, config->remotefilesiglevel);

828
829
830
831
	for(i = config->repos; i; i = alpm_list_next(i)) {
		register_repo(i->data);
	}

832
	if(config->xfercommand) {
833
		alpm_option_set_fetchcb(handle, download_with_xfercommand);
834
	} else if(!(alpm_capabilities() & ALPM_CAPABILITY_DOWNLOADER)) {
835
		pm_printf(ALPM_LOG_WARNING, _("no '%s' configured\n"), "XferCommand");
836
	}
837
838

	if(config->totaldownload) {
839
		alpm_option_set_totaldlcb(handle, cb_dl_total);
840
841
	}

842
843
844
	alpm_option_set_arch(handle, config->arch);
	alpm_option_set_checkspace(handle, config->checkspace);
	alpm_option_set_usesyslog(handle, config->usesyslog);
845

846
	alpm_option_set_ignorepkgs(handle, config->ignorepkg);
847
	alpm_option_set_ignoregroups(handle, config->ignoregrp);
848
849
	alpm_option_set_noupgrades(handle, config->noupgrade);
	alpm_option_set_noextracts(handle, config->noextract);
850

851
852
	alpm_option_set_disable_dl_timeout(handle, config->disable_dl_timeout);

Florian Pritz's avatar
Florian Pritz committed
853
854
855
856
857
858
859
860
861
	for(i = config->assumeinstalled; i; i = i->next) {
		char *entry = i->data;
		alpm_depend_t *dep = alpm_dep_from_string(entry);
		if(!dep) {
			return 1;
		}
		pm_printf(ALPM_LOG_DEBUG, "parsed assume installed: %s %s\n", dep->name, dep->version);

		ret = alpm_option_add_assumeinstalled(handle, dep);
Andrew Gregory's avatar
Andrew Gregory committed
862
		alpm_dep_free(dep);
Florian Pritz's avatar
Florian Pritz committed
863
		if(ret) {
864
			pm_printf(ALPM_LOG_ERROR, _("Failed to pass %s entry to libalpm"), "assume-installed");
Florian Pritz's avatar
Florian Pritz committed
865
866
867
868
			return ret;
		}
	 }

869
	return 0;
Dan McGee's avatar
Dan McGee committed
870
871
}

872
873
874
875
876
/**
 * Allows parsing in advance of an entire config section before we start
 * calling library methods.
 */
struct section_t {
877
	const char *name;
878
	config_repo_t *repo;
879
	int depth;
880
881
};

882
static int process_usage(alpm_list_t *values, int *usage,
883
884
885
		const char *file, int linenum)
{
	alpm_list_t *i;
886
	int level = *usage;
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
	int ret = 0;

	for(i = values; i; i = i->next) {
		char *key = i->data;

		if(strcmp(key, "Sync") == 0) {
			level |= ALPM_DB_USAGE_SYNC;
		} else if(strcmp(key, "Search") == 0) {
			level |= ALPM_DB_USAGE_SEARCH;
		} else if(strcmp(key, "Install") == 0) {
			level |= ALPM_DB_USAGE_INSTALL;
		} else if(strcmp(key, "Upgrade") == 0) {
			level |= ALPM_DB_USAGE_UPGRADE;
		} else if(strcmp(key, "All") == 0) {
			level |= ALPM_DB_USAGE_ALL;
		} else {
			pm_printf(ALPM_LOG_ERROR,
					_("config file %s, line %d: '%s' option '%s' not recognized\n"),
					file, linenum, "Usage", key);
			ret = 1;
		}
	}

	*usage = level;

	return ret;
}


916
917
918
919
static int _parse_repo(const char *key, char *value, const char *file,
		int line, struct section_t *section)
{
	int ret = 0;
920
	config_repo_t *repo = section->repo;
921

922
923
924
925
926
927
928
929
#define CHECK_VALUE(val) do { \
	if(!val) { \
		pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"), \
				file, line, key); \
		return 1; \
	} \
} while(0)

930
	if(strcmp(key, "Server") == 0) {
931
932
		CHECK_VALUE(value);
		repo->servers = alpm_list_add(repo->servers, strdup(value));
933
	} else if(strcmp(key, "SigLevel") == 0) {
934
935
936
937
938
939
940
		CHECK_VALUE(value);
		alpm_list_t *values = NULL;
		setrepeatingoption(value, "SigLevel", &values);
		if(values) {
			ret = process_siglevel(values, &repo->siglevel,
					&repo->siglevel_mask, file, line);
			FREELIST(values);
941
		}
942
	} else if(strcmp(key, "Usage") == 0) {
943
		CHECK_VALUE(value);
944
945
946
		alpm_list_t *values = NULL;
		setrepeatingoption(value, "Usage", &values);
		if(values) {
947
			if(process_usage(values, &repo->usage, file, line)) {
948
949
950
951
952
				FREELIST(values);
				return 1;
			}
			FREELIST(values);
		}
953
954
955
	} else {
		pm_printf(ALPM_LOG_WARNING,
				_("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"),
956
				file, line, key, repo->name);
957
958
	}

959
960
#undef CHECK_VALUE

961
962
963
	return ret;
}

964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
static int _parse_directive(const char *file, int linenum, const char *name,
		char *key, char *value, void *data);

static int process_include(const char *value, void *data,
		const char *file, int linenum)
{
	glob_t globbuf;
	int globret, ret = 0;
	size_t gindex;
	struct section_t *section = data;
	static const int config_max_recursion = 10;

	if(value == NULL) {
		pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"),
				file, linenum, "Include");
		return 1;
	}

	if(section->depth >= config_max_recursion) {
		pm_printf(ALPM_LOG_ERROR,
				_("config parsing exceeded max recursion depth of %d.\n"),
				config_max_recursion);
		return 1;
	}

	section->depth++;

	/* Ignore include failures... assume non-critical */
	globret = glob(value, GLOB_NOCHECK, NULL, &globbuf);
	switch(globret) {
		case GLOB_NOSPACE:
			pm_printf(ALPM_LOG_DEBUG,
					"config file %s, line %d: include globbing out of space\n",
					file, linenum);
			break;
		case GLOB_ABORTED:
			pm_printf(ALPM_LOG_DEBUG,
For faster browsing, not all history is shown. View entire blame