libalpm
Arch Linux Package Manager Library
|
00001 /* 00002 * conf.c 00003 * 00004 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> 00005 * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> 00006 * 00007 * This program is free software; you can redistribute it and/or modify 00008 * it under the terms of the GNU General Public License as published by 00009 * the Free Software Foundation; either version 2 of the License, or 00010 * (at your option) any later version. 00011 * 00012 * This program is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU General Public License 00018 * along with this program. If not, see <http://www.gnu.org/licenses/>. 00019 */ 00020 00021 #include <errno.h> 00022 #include <glob.h> 00023 #include <limits.h> 00024 #include <fcntl.h> /* open */ 00025 #include <stdlib.h> 00026 #include <stdio.h> 00027 #include <string.h> /* strdup */ 00028 #include <sys/stat.h> 00029 #include <sys/types.h> 00030 #include <sys/utsname.h> /* uname */ 00031 #include <unistd.h> 00032 00033 /* pacman */ 00034 #include "conf.h" 00035 #include "util.h" 00036 #include "pacman.h" 00037 #include "callback.h" 00038 00039 /* global config variable */ 00040 config_t *config = NULL; 00041 00042 config_t *config_new(void) 00043 { 00044 config_t *newconfig = calloc(1, sizeof(config_t)); 00045 if(!newconfig) { 00046 pm_printf(ALPM_LOG_ERROR, 00047 _("malloc failure: could not allocate %zd bytes\n"), 00048 sizeof(config_t)); 00049 return NULL; 00050 } 00051 /* defaults which may get overridden later */ 00052 newconfig->op = PM_OP_MAIN; 00053 newconfig->logmask = ALPM_LOG_ERROR | ALPM_LOG_WARNING; 00054 newconfig->configfile = strdup(CONFFILE); 00055 if(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) { 00056 newconfig->siglevel = ALPM_SIG_PACKAGE | ALPM_SIG_PACKAGE_OPTIONAL | 00057 ALPM_SIG_DATABASE | ALPM_SIG_DATABASE_OPTIONAL; 00058 } 00059 00060 return newconfig; 00061 } 00062 00063 int config_free(config_t *oldconfig) 00064 { 00065 if(oldconfig == NULL) { 00066 return -1; 00067 } 00068 00069 alpm_list_free(oldconfig->explicit_adds); 00070 alpm_list_free(oldconfig->explicit_removes); 00071 00072 FREELIST(oldconfig->holdpkg); 00073 FREELIST(oldconfig->syncfirst); 00074 FREELIST(oldconfig->ignorepkg); 00075 FREELIST(oldconfig->ignoregrp); 00076 FREELIST(oldconfig->noupgrade); 00077 FREELIST(oldconfig->noextract); 00078 free(oldconfig->configfile); 00079 free(oldconfig->rootdir); 00080 free(oldconfig->dbpath); 00081 free(oldconfig->logfile); 00082 free(oldconfig->gpgdir); 00083 FREELIST(oldconfig->cachedirs); 00084 free(oldconfig->xfercommand); 00085 free(oldconfig->print_format); 00086 free(oldconfig->arch); 00087 free(oldconfig); 00088 00089 return 0; 00090 } 00091 00092 /** Helper function for download_with_xfercommand() */ 00093 static char *get_filename(const char *url) { 00094 char *filename = strrchr(url, '/'); 00095 if(filename != NULL) { 00096 filename++; 00097 } 00098 return filename; 00099 } 00100 00101 /** Helper function for download_with_xfercommand() */ 00102 static char *get_destfile(const char *path, const char *filename) { 00103 char *destfile; 00104 /* len = localpath len + filename len + null */ 00105 size_t len = strlen(path) + strlen(filename) + 1; 00106 destfile = calloc(len, sizeof(char)); 00107 snprintf(destfile, len, "%s%s", path, filename); 00108 00109 return destfile; 00110 } 00111 00112 /** Helper function for download_with_xfercommand() */ 00113 static char *get_tempfile(const char *path, const char *filename) { 00114 char *tempfile; 00115 /* len = localpath len + filename len + '.part' len + null */ 00116 size_t len = strlen(path) + strlen(filename) + 6; 00117 tempfile = calloc(len, sizeof(char)); 00118 snprintf(tempfile, len, "%s%s.part", path, filename); 00119 00120 return tempfile; 00121 } 00122 00123 /** External fetch callback */ 00124 static int download_with_xfercommand(const char *url, const char *localpath, 00125 int force) { 00126 int ret = 0, retval; 00127 int usepart = 0; 00128 int cwdfd; 00129 struct stat st; 00130 char *parsedcmd,*tempcmd; 00131 char *destfile, *tempfile, *filename; 00132 00133 if(!config->xfercommand) { 00134 return -1; 00135 } 00136 00137 filename = get_filename(url); 00138 if(!filename) { 00139 return -1; 00140 } 00141 destfile = get_destfile(localpath, filename); 00142 tempfile = get_tempfile(localpath, filename); 00143 00144 if(force && stat(tempfile, &st) == 0) { 00145 unlink(tempfile); 00146 } 00147 if(force && stat(destfile, &st) == 0) { 00148 unlink(destfile); 00149 } 00150 00151 tempcmd = strdup(config->xfercommand); 00152 /* replace all occurrences of %o with fn.part */ 00153 if(strstr(tempcmd, "%o")) { 00154 usepart = 1; 00155 parsedcmd = strreplace(tempcmd, "%o", tempfile); 00156 free(tempcmd); 00157 tempcmd = parsedcmd; 00158 } 00159 /* replace all occurrences of %u with the download URL */ 00160 parsedcmd = strreplace(tempcmd, "%u", url); 00161 free(tempcmd); 00162 00163 /* save the cwd so we can restore it later */ 00164 do { 00165 cwdfd = open(".", O_RDONLY); 00166 } while(cwdfd == -1 && errno == EINTR); 00167 if(cwdfd < 0) { 00168 pm_printf(ALPM_LOG_ERROR, _("could not get current working directory\n")); 00169 } 00170 00171 /* cwd to the download directory */ 00172 if(chdir(localpath)) { 00173 pm_printf(ALPM_LOG_WARNING, _("could not chdir to download directory %s\n"), localpath); 00174 ret = -1; 00175 goto cleanup; 00176 } 00177 /* execute the parsed command via /bin/sh -c */ 00178 pm_printf(ALPM_LOG_DEBUG, "running command: %s\n", parsedcmd); 00179 retval = system(parsedcmd); 00180 00181 if(retval == -1) { 00182 pm_printf(ALPM_LOG_WARNING, _("running XferCommand: fork failed!\n")); 00183 ret = -1; 00184 } else if(retval != 0) { 00185 /* download failed */ 00186 pm_printf(ALPM_LOG_DEBUG, "XferCommand command returned non-zero status " 00187 "code (%d)\n", retval); 00188 ret = -1; 00189 } else { 00190 /* download was successful */ 00191 ret = 0; 00192 if(usepart) { 00193 if(rename(tempfile, destfile)) { 00194 pm_printf(ALPM_LOG_ERROR, _("could not rename %s to %s (%s)\n"), 00195 tempfile, destfile, strerror(errno)); 00196 ret = -1; 00197 } 00198 } 00199 } 00200 00201 cleanup: 00202 /* restore the old cwd if we have it */ 00203 if(cwdfd >= 0) { 00204 int ret; 00205 if(fchdir(cwdfd) != 0) { 00206 pm_printf(ALPM_LOG_ERROR, _("could not restore working directory (%s)\n"), 00207 strerror(errno)); 00208 } 00209 do { 00210 ret = close(cwdfd); 00211 } while(ret == -1 && errno == EINTR); 00212 } 00213 00214 if(ret == -1) { 00215 /* hack to let an user the time to cancel a download */ 00216 sleep(2); 00217 } 00218 free(destfile); 00219 free(tempfile); 00220 free(parsedcmd); 00221 00222 return ret; 00223 } 00224 00225 00226 int config_set_arch(const char *arch) 00227 { 00228 if(strcmp(arch, "auto") == 0) { 00229 struct utsname un; 00230 uname(&un); 00231 config->arch = strdup(un.machine); 00232 } else { 00233 config->arch = strdup(arch); 00234 } 00235 pm_printf(ALPM_LOG_DEBUG, "config: arch: %s\n", config->arch); 00236 return 0; 00237 } 00238 00239 /** 00240 * Parse a signature verification level line. 00241 * @param values the list of parsed option values 00242 * @param storage location to store the derived signature level; any existing 00243 * value here is used as a starting point 00244 * @param file path to the config file 00245 * @param linenum current line number in file 00246 * @return 0 on success, 1 on any parsing error 00247 */ 00248 static int process_siglevel(alpm_list_t *values, alpm_siglevel_t *storage, 00249 const char *file, int linenum) 00250 { 00251 alpm_siglevel_t level = *storage; 00252 alpm_list_t *i; 00253 int ret = 0; 00254 00255 /* Collapse the option names into a single bitmasked value */ 00256 for(i = values; i; i = alpm_list_next(i)) { 00257 const char *original = i->data, *value; 00258 int package = 0, database = 0; 00259 00260 if(strncmp(original, "Package", strlen("Package")) == 0) { 00261 /* only packages are affected, don't flip flags for databases */ 00262 value = original + strlen("Package"); 00263 package = 1; 00264 } else if(strncmp(original, "Database", strlen("Database")) == 0) { 00265 /* only databases are affected, don't flip flags for packages */ 00266 value = original + strlen("Database"); 00267 database = 1; 00268 } else { 00269 /* no prefix, so anything found will affect both packages and dbs */ 00270 value = original; 00271 package = database = 1; 00272 } 00273 00274 /* now parse out and store actual flag if it is valid */ 00275 if(strcmp(value, "Never") == 0) { 00276 if(package) { 00277 level &= ~ALPM_SIG_PACKAGE; 00278 } 00279 if(database) { 00280 level &= ~ALPM_SIG_DATABASE; 00281 } 00282 } else if(strcmp(value, "Optional") == 0) { 00283 if(package) { 00284 level |= ALPM_SIG_PACKAGE; 00285 level |= ALPM_SIG_PACKAGE_OPTIONAL; 00286 } 00287 if(database) { 00288 level |= ALPM_SIG_DATABASE; 00289 level |= ALPM_SIG_DATABASE_OPTIONAL; 00290 } 00291 } else if(strcmp(value, "Required") == 0) { 00292 if(package) { 00293 level |= ALPM_SIG_PACKAGE; 00294 level &= ~ALPM_SIG_PACKAGE_OPTIONAL; 00295 } 00296 if(database) { 00297 level |= ALPM_SIG_DATABASE; 00298 level &= ~ALPM_SIG_DATABASE_OPTIONAL; 00299 } 00300 } else if(strcmp(value, "TrustedOnly") == 0) { 00301 if(package) { 00302 level &= ~ALPM_SIG_PACKAGE_MARGINAL_OK; 00303 level &= ~ALPM_SIG_PACKAGE_UNKNOWN_OK; 00304 } 00305 if(database) { 00306 level &= ~ALPM_SIG_DATABASE_MARGINAL_OK; 00307 level &= ~ALPM_SIG_DATABASE_UNKNOWN_OK; 00308 } 00309 } else if(strcmp(value, "TrustAll") == 0) { 00310 if(package) { 00311 level |= ALPM_SIG_PACKAGE_MARGINAL_OK; 00312 level |= ALPM_SIG_PACKAGE_UNKNOWN_OK; 00313 } 00314 if(database) { 00315 level |= ALPM_SIG_DATABASE_MARGINAL_OK; 00316 level |= ALPM_SIG_DATABASE_UNKNOWN_OK; 00317 } 00318 } else { 00319 pm_printf(ALPM_LOG_ERROR, 00320 _("config file %s, line %d: invalid value for '%s' : '%s'\n"), 00321 file, linenum, "SigLevel", original); 00322 ret = 1; 00323 } 00324 level &= ~ALPM_SIG_USE_DEFAULT; 00325 } 00326 00327 /* ensure we have sig checking ability and are actually turning it on */ 00328 if(!(alpm_capabilities() & ALPM_CAPABILITY_SIGNATURES) && 00329 level & (ALPM_SIG_PACKAGE | ALPM_SIG_DATABASE)) { 00330 pm_printf(ALPM_LOG_ERROR, 00331 _("config file %s, line %d: '%s' option invalid, no signature support\n"), 00332 file, linenum, "SigLevel"); 00333 ret = 1; 00334 } 00335 00336 if(!ret) { 00337 *storage = level; 00338 } 00339 return ret; 00340 } 00341 00342 static int process_cleanmethods(alpm_list_t *values, 00343 const char *file, int linenum) 00344 { 00345 alpm_list_t *i; 00346 for(i = values; i; i = alpm_list_next(i)) { 00347 const char *value = i->data; 00348 if(strcmp(value, "KeepInstalled") == 0) { 00349 config->cleanmethod |= PM_CLEAN_KEEPINST; 00350 } else if(strcmp(value, "KeepCurrent") == 0) { 00351 config->cleanmethod |= PM_CLEAN_KEEPCUR; 00352 } else { 00353 pm_printf(ALPM_LOG_ERROR, 00354 _("config file %s, line %d: invalid value for '%s' : '%s'\n"), 00355 file, linenum, "CleanMethod", value); 00356 return 1; 00357 } 00358 } 00359 return 0; 00360 } 00361 00362 /** Add repeating options such as NoExtract, NoUpgrade, etc to libalpm 00363 * settings. Refactored out of the parseconfig code since all of them did 00364 * the exact same thing and duplicated code. 00365 * @param ptr a pointer to the start of the multiple options 00366 * @param option the string (friendly) name of the option, used for messages 00367 * @param list the list to add the option to 00368 */ 00369 static void setrepeatingoption(char *ptr, const char *option, 00370 alpm_list_t **list) 00371 { 00372 char *q; 00373 00374 while((q = strchr(ptr, ' '))) { 00375 *q = '\0'; 00376 *list = alpm_list_add(*list, strdup(ptr)); 00377 pm_printf(ALPM_LOG_DEBUG, "config: %s: %s\n", option, ptr); 00378 ptr = q; 00379 ptr++; 00380 } 00381 *list = alpm_list_add(*list, strdup(ptr)); 00382 pm_printf(ALPM_LOG_DEBUG, "config: %s: %s\n", option, ptr); 00383 } 00384 00385 static int _parse_options(const char *key, char *value, 00386 const char *file, int linenum) 00387 { 00388 if(value == NULL) { 00389 /* options without settings */ 00390 if(strcmp(key, "UseSyslog") == 0) { 00391 config->usesyslog = 1; 00392 pm_printf(ALPM_LOG_DEBUG, "config: usesyslog\n"); 00393 } else if(strcmp(key, "ILoveCandy") == 0) { 00394 config->chomp = 1; 00395 pm_printf(ALPM_LOG_DEBUG, "config: chomp\n"); 00396 } else if(strcmp(key, "VerbosePkgLists") == 0) { 00397 config->verbosepkglists = 1; 00398 pm_printf(ALPM_LOG_DEBUG, "config: verbosepkglists\n"); 00399 } else if(strcmp(key, "UseDelta") == 0) { 00400 config->usedelta = 1; 00401 pm_printf(ALPM_LOG_DEBUG, "config: usedelta\n"); 00402 } else if(strcmp(key, "TotalDownload") == 0) { 00403 config->totaldownload = 1; 00404 pm_printf(ALPM_LOG_DEBUG, "config: totaldownload\n"); 00405 } else if(strcmp(key, "CheckSpace") == 0) { 00406 config->checkspace = 1; 00407 } else { 00408 pm_printf(ALPM_LOG_WARNING, 00409 _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"), 00410 file, linenum, key, "options"); 00411 } 00412 } else { 00413 /* options with settings */ 00414 if(strcmp(key, "NoUpgrade") == 0) { 00415 setrepeatingoption(value, "NoUpgrade", &(config->noupgrade)); 00416 } else if(strcmp(key, "NoExtract") == 0) { 00417 setrepeatingoption(value, "NoExtract", &(config->noextract)); 00418 } else if(strcmp(key, "IgnorePkg") == 0) { 00419 setrepeatingoption(value, "IgnorePkg", &(config->ignorepkg)); 00420 } else if(strcmp(key, "IgnoreGroup") == 0) { 00421 setrepeatingoption(value, "IgnoreGroup", &(config->ignoregrp)); 00422 } else if(strcmp(key, "HoldPkg") == 0) { 00423 setrepeatingoption(value, "HoldPkg", &(config->holdpkg)); 00424 } else if(strcmp(key, "SyncFirst") == 0) { 00425 setrepeatingoption(value, "SyncFirst", &(config->syncfirst)); 00426 } else if(strcmp(key, "CacheDir") == 0) { 00427 setrepeatingoption(value, "CacheDir", &(config->cachedirs)); 00428 } else if(strcmp(key, "Architecture") == 0) { 00429 if(!config->arch) { 00430 config_set_arch(value); 00431 } 00432 } else if(strcmp(key, "DBPath") == 0) { 00433 /* don't overwrite a path specified on the command line */ 00434 if(!config->dbpath) { 00435 config->dbpath = strdup(value); 00436 pm_printf(ALPM_LOG_DEBUG, "config: dbpath: %s\n", value); 00437 } 00438 } else if(strcmp(key, "RootDir") == 0) { 00439 /* don't overwrite a path specified on the command line */ 00440 if(!config->rootdir) { 00441 config->rootdir = strdup(value); 00442 pm_printf(ALPM_LOG_DEBUG, "config: rootdir: %s\n", value); 00443 } 00444 } else if(strcmp(key, "GPGDir") == 0) { 00445 if(!config->gpgdir) { 00446 config->gpgdir = strdup(value); 00447 pm_printf(ALPM_LOG_DEBUG, "config: gpgdir: %s\n", value); 00448 } 00449 } else if(strcmp(key, "LogFile") == 0) { 00450 if(!config->logfile) { 00451 config->logfile = strdup(value); 00452 pm_printf(ALPM_LOG_DEBUG, "config: logfile: %s\n", value); 00453 } 00454 } else if(strcmp(key, "XferCommand") == 0) { 00455 config->xfercommand = strdup(value); 00456 pm_printf(ALPM_LOG_DEBUG, "config: xfercommand: %s\n", value); 00457 } else if(strcmp(key, "CleanMethod") == 0) { 00458 alpm_list_t *methods = NULL; 00459 setrepeatingoption(value, "CleanMethod", &methods); 00460 if(process_cleanmethods(methods, file, linenum)) { 00461 FREELIST(methods); 00462 return 1; 00463 } 00464 FREELIST(methods); 00465 } else if(strcmp(key, "SigLevel") == 0) { 00466 alpm_list_t *values = NULL; 00467 setrepeatingoption(value, "SigLevel", &values); 00468 if(process_siglevel(values, &config->siglevel, file, linenum)) { 00469 FREELIST(values); 00470 return 1; 00471 } 00472 FREELIST(values); 00473 } else { 00474 pm_printf(ALPM_LOG_WARNING, 00475 _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"), 00476 file, linenum, key, "options"); 00477 } 00478 00479 } 00480 return 0; 00481 } 00482 00483 static int _add_mirror(alpm_db_t *db, char *value) 00484 { 00485 const char *dbname = alpm_db_get_name(db); 00486 /* let's attempt a replacement for the current repo */ 00487 char *temp = strreplace(value, "$repo", dbname); 00488 /* let's attempt a replacement for the arch */ 00489 const char *arch = config->arch; 00490 char *server; 00491 if(arch) { 00492 server = strreplace(temp, "$arch", arch); 00493 free(temp); 00494 } else { 00495 if(strstr(temp, "$arch")) { 00496 free(temp); 00497 pm_printf(ALPM_LOG_ERROR, 00498 _("mirror '%s' contains the '%s' variable, but no '%s' is defined.\n"), 00499 value, "$arch", "Architecture"); 00500 return 1; 00501 } 00502 server = temp; 00503 } 00504 00505 if(alpm_db_add_server(db, server) != 0) { 00506 /* pm_errno is set by alpm_db_setserver */ 00507 pm_printf(ALPM_LOG_ERROR, _("could not add server URL to database '%s': %s (%s)\n"), 00508 dbname, server, alpm_strerror(alpm_errno(config->handle))); 00509 free(server); 00510 return 1; 00511 } 00512 00513 free(server); 00514 return 0; 00515 } 00516 00517 /** Sets up libalpm global stuff in one go. Called after the command line 00518 * and inital config file parsing. Once this is complete, we can see if any 00519 * paths were defined. If a rootdir was defined and nothing else, we want all 00520 * of our paths to live under the rootdir that was specified. Safe to call 00521 * multiple times (will only do anything the first time). 00522 */ 00523 static int setup_libalpm(void) 00524 { 00525 int ret = 0; 00526 alpm_errno_t err; 00527 alpm_handle_t *handle; 00528 00529 pm_printf(ALPM_LOG_DEBUG, "setup_libalpm called\n"); 00530 00531 /* Configure root path first. If it is set and dbpath/logfile were not 00532 * set, then set those as well to reside under the root. */ 00533 if(config->rootdir) { 00534 char path[PATH_MAX]; 00535 if(!config->dbpath) { 00536 snprintf(path, PATH_MAX, "%s/%s", config->rootdir, DBPATH + 1); 00537 config->dbpath = strdup(path); 00538 } 00539 if(!config->logfile) { 00540 snprintf(path, PATH_MAX, "%s/%s", config->rootdir, LOGFILE + 1); 00541 config->logfile = strdup(path); 00542 } 00543 } else { 00544 config->rootdir = strdup(ROOTDIR); 00545 if(!config->dbpath) { 00546 config->dbpath = strdup(DBPATH); 00547 } 00548 } 00549 00550 /* initialize library */ 00551 handle = alpm_initialize(config->rootdir, config->dbpath, &err); 00552 if(!handle) { 00553 pm_printf(ALPM_LOG_ERROR, _("failed to initialize alpm library (%s)\n"), 00554 alpm_strerror(err)); 00555 if(err == ALPM_ERR_DB_VERSION) { 00556 pm_printf(ALPM_LOG_ERROR, _(" try running pacman-db-upgrade\n")); 00557 } 00558 return -1; 00559 } 00560 config->handle = handle; 00561 00562 alpm_option_set_logcb(handle, cb_log); 00563 alpm_option_set_dlcb(handle, cb_dl_progress); 00564 alpm_option_set_eventcb(handle, cb_event); 00565 alpm_option_set_questioncb(handle, cb_question); 00566 alpm_option_set_progresscb(handle, cb_progress); 00567 00568 config->logfile = config->logfile ? config->logfile : strdup(LOGFILE); 00569 ret = alpm_option_set_logfile(handle, config->logfile); 00570 if(ret != 0) { 00571 pm_printf(ALPM_LOG_ERROR, _("problem setting logfile '%s' (%s)\n"), 00572 config->logfile, alpm_strerror(alpm_errno(handle))); 00573 return ret; 00574 } 00575 00576 /* Set GnuPG's home directory. This is not relative to rootdir, even if 00577 * rootdir is defined. Reasoning: gpgdir contains configuration data. */ 00578 config->gpgdir = config->gpgdir ? config->gpgdir : strdup(GPGDIR); 00579 ret = alpm_option_set_gpgdir(handle, config->gpgdir); 00580 if(ret != 0) { 00581 pm_printf(ALPM_LOG_ERROR, _("problem setting gpgdir '%s' (%s)\n"), 00582 config->gpgdir, alpm_strerror(alpm_errno(handle))); 00583 return ret; 00584 } 00585 00586 /* add a default cachedir if one wasn't specified */ 00587 if(config->cachedirs == NULL) { 00588 alpm_option_add_cachedir(handle, CACHEDIR); 00589 } else { 00590 alpm_option_set_cachedirs(handle, config->cachedirs); 00591 } 00592 00593 alpm_option_set_default_siglevel(handle, config->siglevel); 00594 00595 if(config->xfercommand) { 00596 alpm_option_set_fetchcb(handle, download_with_xfercommand); 00597 } else if(!(alpm_capabilities() & ALPM_CAPABILITY_DOWNLOADER)) { 00598 pm_printf(ALPM_LOG_WARNING, _("no '%s' configured"), "XferCommand"); 00599 } 00600 00601 if(config->totaldownload) { 00602 alpm_option_set_totaldlcb(handle, cb_dl_total); 00603 } 00604 00605 alpm_option_set_arch(handle, config->arch); 00606 alpm_option_set_checkspace(handle, config->checkspace); 00607 alpm_option_set_usesyslog(handle, config->usesyslog); 00608 alpm_option_set_usedelta(handle, config->usedelta); 00609 00610 alpm_option_set_ignorepkgs(handle, config->ignorepkg); 00611 alpm_option_set_ignoregroups(handle, config->ignoregrp); 00612 alpm_option_set_noupgrades(handle, config->noupgrade); 00613 alpm_option_set_noextracts(handle, config->noextract); 00614 00615 return 0; 00616 } 00617 00618 /** 00619 * Allows parsing in advance of an entire config section before we start 00620 * calling library methods. 00621 */ 00622 struct section_t { 00623 /* useful for all sections */ 00624 char *name; 00625 int is_options; 00626 /* db section option gathering */ 00627 alpm_siglevel_t siglevel; 00628 alpm_list_t *servers; 00629 }; 00630 00631 /** 00632 * Wrap up a section once we have reached the end of it. This should be called 00633 * when a subsequent section is encountered, or when we have reached the end of 00634 * the root config file. Once called, all existing saved config pieces on the 00635 * section struct are freed. 00636 * @param section the current parsed and saved section data 00637 * @param parse_options whether we are parsing options or repo data 00638 * @return 0 on success, 1 on failure 00639 */ 00640 static int finish_section(struct section_t *section, int parse_options) 00641 { 00642 int ret = 0; 00643 alpm_list_t *i; 00644 alpm_db_t *db; 00645 00646 pm_printf(ALPM_LOG_DEBUG, "config: finish section '%s'\n", section->name); 00647 00648 /* parsing options (or nothing)- nothing to do except free the pieces */ 00649 if(!section->name || parse_options || section->is_options) { 00650 goto cleanup; 00651 } 00652 00653 /* if we are not looking at options sections only, register a db */ 00654 db = alpm_db_register_sync(config->handle, section->name, section->siglevel); 00655 if(db == NULL) { 00656 pm_printf(ALPM_LOG_ERROR, _("could not register '%s' database (%s)\n"), 00657 section->name, alpm_strerror(alpm_errno(config->handle))); 00658 ret = 1; 00659 goto cleanup; 00660 } 00661 00662 for(i = section->servers; i; i = alpm_list_next(i)) { 00663 char *value = i->data; 00664 if(_add_mirror(db, value) != 0) { 00665 pm_printf(ALPM_LOG_ERROR, 00666 _("could not add mirror '%s' to database '%s' (%s)\n"), 00667 value, section->name, alpm_strerror(alpm_errno(config->handle))); 00668 ret = 1; 00669 goto cleanup; 00670 } 00671 free(value); 00672 } 00673 00674 cleanup: 00675 alpm_list_free(section->servers); 00676 section->servers = NULL; 00677 section->siglevel = ALPM_SIG_USE_DEFAULT; 00678 free(section->name); 00679 section->name = NULL; 00680 return ret; 00681 } 00682 00683 /** The "real" parseconfig. Each "Include" directive will recall this method so 00684 * recursion and stack depth are limited to 10 levels. The publicly visible 00685 * parseconfig calls this with a NULL section argument so we can recall from 00686 * within ourself on an include. 00687 * @param file path to the config file 00688 * @param section the current active section 00689 * @param parse_options whether to parse and call methods for the options 00690 * section; if 0, parse and call methods for the repos sections 00691 * @param depth the current recursion depth 00692 * @return 0 on success, 1 on failure 00693 */ 00694 static int _parseconfig(const char *file, struct section_t *section, 00695 int parse_options, int depth) 00696 { 00697 FILE *fp = NULL; 00698 char line[PATH_MAX]; 00699 int linenum = 0; 00700 int ret = 0; 00701 const int max_depth = 10; 00702 00703 if(depth >= max_depth) { 00704 pm_printf(ALPM_LOG_ERROR, 00705 _("config parsing exceeded max recursion depth of %d.\n"), max_depth); 00706 ret = 1; 00707 goto cleanup; 00708 } 00709 00710 pm_printf(ALPM_LOG_DEBUG, "config: attempting to read file %s\n", file); 00711 fp = fopen(file, "r"); 00712 if(fp == NULL) { 00713 pm_printf(ALPM_LOG_ERROR, _("config file %s could not be read.\n"), file); 00714 ret = 1; 00715 goto cleanup; 00716 } 00717 00718 while(fgets(line, PATH_MAX, fp)) { 00719 char *key, *value, *ptr; 00720 size_t line_len; 00721 00722 linenum++; 00723 00724 /* ignore whole line and end of line comments */ 00725 if((ptr = strchr(line, '#'))) { 00726 *ptr = '\0'; 00727 } 00728 00729 line_len = strtrim(line); 00730 00731 if(line_len == 0) { 00732 continue; 00733 } 00734 00735 if(line[0] == '[' && line[line_len - 1] == ']') { 00736 char *name; 00737 /* only possibility here is a line == '[]' */ 00738 if(line_len <= 2) { 00739 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: bad section name.\n"), 00740 file, linenum); 00741 ret = 1; 00742 goto cleanup; 00743 } 00744 /* new config section, skip the '[' */ 00745 name = strdup(line + 1); 00746 name[line_len - 2] = '\0'; 00747 /* we're at a new section; perform any post-actions for the prior */ 00748 if(finish_section(section, parse_options)) { 00749 ret = 1; 00750 goto cleanup; 00751 } 00752 pm_printf(ALPM_LOG_DEBUG, "config: new section '%s'\n", name); 00753 section->name = name; 00754 section->is_options = (strcmp(name, "options") == 0); 00755 continue; 00756 } 00757 00758 /* directive */ 00759 /* strsep modifies the 'line' string: 'key \0 value' */ 00760 key = line; 00761 value = line; 00762 strsep(&value, "="); 00763 strtrim(key); 00764 strtrim(value); 00765 00766 if(key == NULL) { 00767 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: syntax error in config file- missing key.\n"), 00768 file, linenum); 00769 ret = 1; 00770 goto cleanup; 00771 } 00772 /* For each directive, compare to the camelcase string. */ 00773 if(section->name == NULL) { 00774 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: All directives must belong to a section.\n"), 00775 file, linenum); 00776 ret = 1; 00777 goto cleanup; 00778 } 00779 /* Include is allowed in both options and repo sections */ 00780 if(strcmp(key, "Include") == 0) { 00781 glob_t globbuf; 00782 int globret; 00783 size_t gindex; 00784 00785 if(value == NULL) { 00786 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"), 00787 file, linenum, key); 00788 ret = 1; 00789 goto cleanup; 00790 } 00791 /* Ignore include failures... assume non-critical */ 00792 globret = glob(value, GLOB_NOCHECK, NULL, &globbuf); 00793 switch(globret) { 00794 case GLOB_NOSPACE: 00795 pm_printf(ALPM_LOG_DEBUG, 00796 "config file %s, line %d: include globbing out of space\n", 00797 file, linenum); 00798 break; 00799 case GLOB_ABORTED: 00800 pm_printf(ALPM_LOG_DEBUG, 00801 "config file %s, line %d: include globbing read error for %s\n", 00802 file, linenum, value); 00803 break; 00804 case GLOB_NOMATCH: 00805 pm_printf(ALPM_LOG_DEBUG, 00806 "config file %s, line %d: no include found for %s\n", 00807 file, linenum, value); 00808 break; 00809 default: 00810 for(gindex = 0; gindex < globbuf.gl_pathc; gindex++) { 00811 pm_printf(ALPM_LOG_DEBUG, "config file %s, line %d: including %s\n", 00812 file, linenum, globbuf.gl_pathv[gindex]); 00813 _parseconfig(globbuf.gl_pathv[gindex], section, parse_options, depth + 1); 00814 } 00815 break; 00816 } 00817 globfree(&globbuf); 00818 continue; 00819 } 00820 if(parse_options && section->is_options) { 00821 /* we are either in options ... */ 00822 if((ret = _parse_options(key, value, file, linenum)) != 0) { 00823 goto cleanup; 00824 } 00825 } else if(!parse_options && !section->is_options) { 00826 /* ... or in a repo section */ 00827 if(strcmp(key, "Server") == 0) { 00828 if(value == NULL) { 00829 pm_printf(ALPM_LOG_ERROR, _("config file %s, line %d: directive '%s' needs a value\n"), 00830 file, linenum, key); 00831 ret = 1; 00832 goto cleanup; 00833 } 00834 section->servers = alpm_list_add(section->servers, strdup(value)); 00835 } else if(strcmp(key, "SigLevel") == 0) { 00836 alpm_list_t *values = NULL; 00837 setrepeatingoption(value, "SigLevel", &values); 00838 if(values) { 00839 if(section->siglevel == ALPM_SIG_USE_DEFAULT) { 00840 section->siglevel = config->siglevel; 00841 } 00842 if(process_siglevel(values, §ion->siglevel, file, linenum)) { 00843 FREELIST(values); 00844 ret = 1; 00845 goto cleanup; 00846 } 00847 FREELIST(values); 00848 } 00849 } else { 00850 pm_printf(ALPM_LOG_WARNING, 00851 _("config file %s, line %d: directive '%s' in section '%s' not recognized.\n"), 00852 file, linenum, key, section->name); 00853 } 00854 } 00855 } 00856 00857 if(depth == 0) { 00858 ret = finish_section(section, parse_options); 00859 } 00860 00861 cleanup: 00862 if(fp) { 00863 fclose(fp); 00864 } 00865 pm_printf(ALPM_LOG_DEBUG, "config: finished parsing %s\n", file); 00866 return ret; 00867 } 00868 00869 /** Parse a configuration file. 00870 * @param file path to the config file 00871 * @return 0 on success, non-zero on error 00872 */ 00873 int parseconfig(const char *file) 00874 { 00875 int ret; 00876 struct section_t section; 00877 memset(§ion, 0, sizeof(struct section_t)); 00878 section.siglevel = ALPM_SIG_USE_DEFAULT; 00879 /* the config parse is a two-pass affair. We first parse the entire thing for 00880 * the [options] section so we can get all default and path options set. 00881 * Next, we go back and parse everything but [options]. */ 00882 00883 /* call the real parseconfig function with a null section & db argument */ 00884 pm_printf(ALPM_LOG_DEBUG, "parseconfig: options pass\n"); 00885 if((ret = _parseconfig(file, §ion, 1, 0))) { 00886 return ret; 00887 } 00888 if((ret = setup_libalpm())) { 00889 return ret; 00890 } 00891 /* second pass, repo section parsing */ 00892 pm_printf(ALPM_LOG_DEBUG, "parseconfig: repo pass\n"); 00893 return _parseconfig(file, §ion, 0, 0); 00894 } 00895 00896 /* vim: set ts=2 sw=2 noet: */