1 /* opkg_conf.c - the opkg package management system 2 3 Copyright (C) 2009 Ubiq Technologies <graham.gower@gmail.com> 4 5 Carl D. Worth 6 Copyright (C) 2001 University of Southern California 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License as 10 published by the Free Software Foundation; either version 2, or (at 11 your option) any later version. 12 13 This program is distributed in the hope that it will be useful, but 14 WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 General Public License for more details. 17 */ 18 19 #include <stdio.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <fcntl.h> 23 #include <glob.h> 24 #include <unistd.h> 25 26 #include "opkg_conf.h" 27 #include "pkg_vec.h" 28 #include "pkg.h" 29 #include "xregex.h" 30 #include "sprintf_alloc.h" 31 #include "opkg_message.h" 32 #include "file_util.h" 33 #include "opkg_defines.h" 34 #include "libbb/libbb.h" 35 36 static int lock_fd; 37 static char *lock_file = NULL; 38 39 static opkg_conf_t _conf; 40 opkg_conf_t *conf = &_conf; 41 42 /* 43 * Config file options 44 */ 45 opkg_option_t options[] = { 46 {"cache", OPKG_OPT_TYPE_STRING, &_conf.cache}, 47 {"force_defaults", OPKG_OPT_TYPE_BOOL, &_conf.force_defaults}, 48 {"force_maintainer", OPKG_OPT_TYPE_BOOL, &_conf.force_maintainer}, 49 {"force_depends", OPKG_OPT_TYPE_BOOL, &_conf.force_depends}, 50 {"force_overwrite", OPKG_OPT_TYPE_BOOL, &_conf.force_overwrite}, 51 {"force_downgrade", OPKG_OPT_TYPE_BOOL, &_conf.force_downgrade}, 52 {"force_reinstall", OPKG_OPT_TYPE_BOOL, &_conf.force_reinstall}, 53 {"force_space", OPKG_OPT_TYPE_BOOL, &_conf.force_space}, 54 {"force_postinstall", OPKG_OPT_TYPE_BOOL, &_conf.force_postinstall}, 55 {"force_checksum", OPKG_OPT_TYPE_BOOL, &_conf.force_checksum}, 56 {"check_signature", OPKG_OPT_TYPE_BOOL, &_conf.check_signature}, 57 {"no_check_certificate", OPKG_OPT_TYPE_BOOL, &_conf.no_check_certificate}, 58 {"ftp_proxy", OPKG_OPT_TYPE_STRING, &_conf.ftp_proxy}, 59 {"http_proxy", OPKG_OPT_TYPE_STRING, &_conf.http_proxy}, 60 {"http_timeout", OPKG_OPT_TYPE_STRING, &_conf.http_timeout}, 61 {"https_proxy", OPKG_OPT_TYPE_STRING, &_conf.https_proxy}, 62 {"no_proxy", OPKG_OPT_TYPE_STRING, &_conf.no_proxy}, 63 {"test", OPKG_OPT_TYPE_BOOL, &_conf.noaction}, 64 {"noaction", OPKG_OPT_TYPE_BOOL, &_conf.noaction}, 65 {"download_only", OPKG_OPT_TYPE_BOOL, &_conf.download_only}, 66 {"nodeps", OPKG_OPT_TYPE_BOOL, &_conf.nodeps}, 67 {"nocase", OPKG_OPT_TYPE_BOOL, &_conf.nocase}, 68 {"offline_root", OPKG_OPT_TYPE_STRING, &_conf.offline_root}, 69 {"overlay_root", OPKG_OPT_TYPE_STRING, &_conf.overlay_root}, 70 {"proxy_passwd", OPKG_OPT_TYPE_STRING, &_conf.proxy_passwd}, 71 {"proxy_user", OPKG_OPT_TYPE_STRING, &_conf.proxy_user}, 72 {"query-all", OPKG_OPT_TYPE_BOOL, &_conf.query_all}, 73 {"size", OPKG_OPT_TYPE_BOOL, &_conf.size}, 74 {"strip_abi", OPKG_OPT_TYPE_BOOL, &_conf.strip_abi}, 75 {"tmp_dir", OPKG_OPT_TYPE_STRING, &_conf.tmp_dir}, 76 {"verbosity", OPKG_OPT_TYPE_INT, &_conf.verbosity}, 77 {"verify_program", OPKG_OPT_TYPE_STRING, &_conf.verify_program}, 78 {NULL, 0, NULL} 79 }; 80 81 static int resolve_pkg_dest_list(void) 82 { 83 nv_pair_list_elt_t *iter; 84 nv_pair_t *nv_pair; 85 pkg_dest_t *dest; 86 char *root_dir; 87 88 for (iter = nv_pair_list_first(&conf->tmp_dest_list); iter; 89 iter = nv_pair_list_next(&conf->tmp_dest_list, iter)) { 90 nv_pair = (nv_pair_t *) iter->data; 91 92 if (conf->offline_root) { 93 sprintf_alloc(&root_dir, "%s%s", conf->offline_root, 94 nv_pair->value); 95 } else { 96 root_dir = xstrdup(nv_pair->value); 97 } 98 99 dest = 100 pkg_dest_list_append(&conf->pkg_dest_list, nv_pair->name, 101 root_dir, conf->lists_dir); 102 free(root_dir); 103 104 if (conf->default_dest == NULL) 105 conf->default_dest = dest; 106 107 if (conf->dest_str && !strcmp(dest->name, conf->dest_str)) { 108 conf->default_dest = dest; 109 conf->restrict_to_default_dest = 1; 110 } 111 } 112 113 if (conf->dest_str && !conf->restrict_to_default_dest) { 114 opkg_msg(ERROR, "Unknown dest name: `%s'.\n", conf->dest_str); 115 return -1; 116 } 117 118 return 0; 119 } 120 121 static int opkg_conf_set_option(const char *name, const char *value) 122 { 123 int i = 0; 124 125 while (options[i].name) { 126 if (strcmp(options[i].name, name) == 0) { 127 switch (options[i].type) { 128 case OPKG_OPT_TYPE_BOOL: 129 if (*(int *)options[i].value) { 130 opkg_msg(ERROR, 131 "Duplicate boolean option %s, " 132 "leaving this option on.\n", 133 name); 134 return 0; 135 } 136 *((int *const)options[i].value) = 1; 137 return 0; 138 case OPKG_OPT_TYPE_INT: 139 if (value) { 140 if (*(int *)options[i].value) { 141 opkg_msg(ERROR, 142 "Duplicate option %s, " 143 "using first seen value \"%d\".\n", 144 name, 145 *((int *)options[i]. 146 value)); 147 return 0; 148 } 149 *((int *const)options[i].value) = 150 atoi(value); 151 return 0; 152 } else { 153 opkg_msg(ERROR, 154 "Option %s needs an argument\n", 155 name); 156 return -1; 157 } 158 case OPKG_OPT_TYPE_STRING: 159 if (value) { 160 if (*(char **)options[i].value) { 161 opkg_msg(ERROR, 162 "Duplicate option %s, " 163 "using first seen value \"%s\".\n", 164 name, 165 *((char **)options[i]. 166 value)); 167 return 0; 168 } 169 *((char **const)options[i].value) = 170 xstrdup(value); 171 return 0; 172 } else { 173 opkg_msg(ERROR, 174 "Option %s needs an argument\n", 175 name); 176 return -1; 177 } 178 } 179 } 180 i++; 181 } 182 183 opkg_msg(ERROR, "Unrecognized option: %s=%s\n", name, value); 184 return -1; 185 } 186 187 static int 188 opkg_conf_parse_file(const char *filename, 189 pkg_src_list_t * pkg_src_list) 190 { 191 int line_num = 0; 192 int err = 0; 193 FILE *file; 194 regex_t valid_line_re, comment_re; 195 #define regmatch_size 14 196 regmatch_t regmatch[regmatch_size]; 197 198 file = fopen(filename, "r"); 199 if (file == NULL) { 200 opkg_perror(ERROR, "Failed to open %s", filename); 201 err = -1; 202 goto err0; 203 } 204 205 opkg_msg(INFO, "Loading conf file %s.\n", filename); 206 207 err = xregcomp(&comment_re, 208 "^[[:space:]]*(#.*|[[:space:]]*)$", REG_EXTENDED); 209 if (err) 210 goto err1; 211 212 err = xregcomp(&valid_line_re, 213 "^[[:space:]]*(\"([^\"]*)\"|([^[:space:]]*))" 214 "[[:space:]]*(\"([^\"]*)\"|([^[:space:]]*))" 215 "[[:space:]]*(\"([^\"]*)\"|([^[:space:]]*))" 216 "([[:space:]]+([^[:space:]]+))?([[:space:]]+(.*))?[[:space:]]*$", 217 REG_EXTENDED); 218 if (err) 219 goto err2; 220 221 while (1) { 222 char *line; 223 char *type, *name, *value; 224 225 line_num++; 226 227 line = file_read_line_alloc(file); 228 if (line == NULL) 229 break; 230 231 if (regexec(&comment_re, line, 0, 0, 0) == 0) 232 goto NEXT_LINE; 233 234 if (regexec(&valid_line_re, line, regmatch_size, regmatch, 0) == 235 REG_NOMATCH) { 236 opkg_msg(ERROR, "%s:%d: Ignoring invalid line: `%s'\n", 237 filename, line_num, line); 238 goto NEXT_LINE; 239 } 240 241 /* This has to be so ugly to deal with optional quotation marks */ 242 if (regmatch[2].rm_so > 0) { 243 type = xstrndup(line + regmatch[2].rm_so, 244 regmatch[2].rm_eo - regmatch[2].rm_so); 245 } else { 246 type = xstrndup(line + regmatch[3].rm_so, 247 regmatch[3].rm_eo - regmatch[3].rm_so); 248 } 249 250 if (regmatch[5].rm_so > 0) { 251 name = xstrndup(line + regmatch[5].rm_so, 252 regmatch[5].rm_eo - regmatch[5].rm_so); 253 } else { 254 name = xstrndup(line + regmatch[6].rm_so, 255 regmatch[6].rm_eo - regmatch[6].rm_so); 256 } 257 258 if (regmatch[8].rm_so > 0) { 259 value = xstrndup(line + regmatch[8].rm_so, 260 regmatch[8].rm_eo - regmatch[8].rm_so); 261 } else { 262 value = xstrndup(line + regmatch[9].rm_so, 263 regmatch[9].rm_eo - regmatch[9].rm_so); 264 } 265 266 if (regmatch[13].rm_so != regmatch[13].rm_eo 267 && strncmp(type, "dist", 4) != 0) { 268 opkg_msg(ERROR, 269 "%s:%d: Ignoring config line with trailing garbage: `%s'\n", 270 filename, line_num, line); 271 } else { 272 273 /* We use the conf->tmp_dest_list below instead of 274 conf->pkg_dest_list because we might encounter an 275 offline_root option later and that would invalidate the 276 directories we would have computed in 277 pkg_dest_list_init. (We do a similar thing with 278 tmp_src_nv_pair_list for sake of symmetry.) */ 279 if (strcmp(type, "option") == 0) { 280 opkg_conf_set_option(name, value); 281 } else if (strcmp(type, "src") == 0) { 282 if (!nv_pair_list_find 283 ((nv_pair_list_t *) pkg_src_list, name)) { 284 pkg_src_list_append(pkg_src_list, name, 285 value, 0); 286 } else { 287 opkg_msg(ERROR, 288 "Duplicate src declaration (%s %s). " 289 "Skipping.\n", name, value); 290 } 291 } else if (strcmp(type, "src/gz") == 0) { 292 if (!nv_pair_list_find 293 ((nv_pair_list_t *) pkg_src_list, name)) { 294 pkg_src_list_append(pkg_src_list, name, 295 value, 1); 296 } else { 297 opkg_msg(ERROR, 298 "Duplicate src declaration (%s %s). " 299 "Skipping.\n", name, value); 300 } 301 } else if (strcmp(type, "dest") == 0) { 302 nv_pair_list_append(&conf->tmp_dest_list, name, 303 value); 304 } else if (strcmp(type, "lists_dir") == 0) { 305 conf->lists_dir = xstrdup(value); 306 } else if (strcmp(type, "arch") == 0) { 307 opkg_msg(INFO, 308 "Supported arch %s priority (%s)\n", 309 name, value); 310 if (!value) { 311 opkg_msg(NOTICE, 312 "No priority given for architecture %s," 313 "defaulting to 10\n", name); 314 value = xstrdup("10"); 315 } 316 nv_pair_list_append(&conf->arch_list, name, 317 value); 318 } else { 319 opkg_msg(ERROR, 320 "%s:%d: Ignoring invalid line: `%s'\n", 321 filename, line_num, line); 322 } 323 324 } 325 326 free(type); 327 free(name); 328 free(value); 329 330 NEXT_LINE: 331 free(line); 332 } 333 334 regfree(&valid_line_re); 335 err2: 336 regfree(&comment_re); 337 err1: 338 if (fclose(file) == EOF) { 339 opkg_perror(ERROR, "Couldn't close %s", filename); 340 err = -1; 341 } 342 err0: 343 return err; 344 } 345 346 int opkg_conf_write_status_files(void) 347 { 348 pkg_dest_list_elt_t *iter; 349 pkg_dest_t *dest; 350 pkg_vec_t *all; 351 pkg_t *pkg; 352 int i, ret = 0; 353 354 if (conf->noaction) 355 return 0; 356 357 list_for_each_entry(iter, &conf->pkg_dest_list.head, node) { 358 dest = (pkg_dest_t *) iter->data; 359 360 dest->status_fp = fopen(dest->status_file_name, "w"); 361 if (dest->status_fp == NULL && errno != EROFS) { 362 opkg_perror(ERROR, "Can't open status file %s", 363 dest->status_file_name); 364 ret = -1; 365 } 366 } 367 368 all = pkg_vec_alloc(); 369 pkg_hash_fetch_available(all); 370 371 for (i = 0; i < all->len; i++) { 372 pkg = all->pkgs[i]; 373 /* We don't need most uninstalled packages in the status file */ 374 if (pkg->state_status == SS_NOT_INSTALLED 375 && (pkg->state_want == SW_UNKNOWN 376 || (pkg->state_want == SW_DEINSTALL 377 && pkg->state_flag != SF_HOLD) 378 || pkg->state_want == SW_PURGE)) { 379 continue; 380 } 381 if (pkg->dest == NULL) { 382 opkg_msg(ERROR, 383 "Internal error: package %s has a NULL dest\n", 384 pkg->name); 385 continue; 386 } 387 if (pkg->dest->status_fp) 388 pkg_print_status(pkg, pkg->dest->status_fp); 389 } 390 391 pkg_vec_free(all); 392 393 list_for_each_entry(iter, &conf->pkg_dest_list.head, node) { 394 dest = (pkg_dest_t *) iter->data; 395 if (dest->status_fp && fclose(dest->status_fp) == EOF) { 396 opkg_perror(ERROR, "Couldn't close %s", 397 dest->status_file_name); 398 ret = -1; 399 } 400 } 401 402 return ret; 403 } 404 405 char *root_filename_alloc(char *filename) 406 { 407 char *root_filename; 408 sprintf_alloc(&root_filename, "%s%s", 409 (conf->offline_root ? conf->offline_root : ""), filename); 410 return root_filename; 411 } 412 413 static int glob_errfunc(const char *epath, int eerrno) 414 { 415 if (eerrno == ENOENT) 416 /* If leading dir does not exist, we get GLOB_NOMATCH. */ 417 return 0; 418 419 opkg_msg(ERROR, "glob failed for %s: %s\n", epath, strerror(eerrno)); 420 return 0; 421 } 422 423 int opkg_conf_init(void) 424 { 425 pkg_src_list_init(&conf->pkg_src_list); 426 pkg_dest_list_init(&conf->pkg_dest_list); 427 pkg_dest_list_init(&conf->tmp_dest_list); 428 nv_pair_list_init(&conf->arch_list); 429 430 return 0; 431 } 432 433 int opkg_conf_load(void) 434 { 435 int i, glob_ret; 436 char *tmp, *tmp_dir_base, **tmp_val; 437 glob_t globbuf; 438 char *etc_opkg_conf_pattern; 439 440 conf->restrict_to_default_dest = 0; 441 conf->default_dest = NULL; 442 443 if (!conf->offline_root) 444 conf->offline_root = xstrdup(getenv("OFFLINE_ROOT")); 445 446 if (conf->conf_file) { 447 struct stat st; 448 if (stat(conf->conf_file, &st) == -1) { 449 opkg_perror(ERROR, "Couldn't stat %s", conf->conf_file); 450 goto err0; 451 } 452 if (opkg_conf_parse_file(conf->conf_file, 453 &conf->pkg_src_list)) 454 goto err1; 455 } 456 457 if (conf->offline_root) 458 sprintf_alloc(&etc_opkg_conf_pattern, "%s/etc/opkg/*.conf", 459 conf->offline_root); 460 else { 461 const char *conf_file_dir = getenv("OPKG_CONF_DIR"); 462 if (conf_file_dir == NULL) 463 conf_file_dir = OPKG_CONF_DEFAULT_CONF_FILE_DIR; 464 sprintf_alloc(&etc_opkg_conf_pattern, "%s/*.conf", 465 conf_file_dir); 466 } 467 468 memset(&globbuf, 0, sizeof(globbuf)); 469 glob_ret = glob(etc_opkg_conf_pattern, 0, glob_errfunc, &globbuf); 470 if (glob_ret && glob_ret != GLOB_NOMATCH) { 471 free(etc_opkg_conf_pattern); 472 globfree(&globbuf); 473 goto err1; 474 } 475 476 free(etc_opkg_conf_pattern); 477 478 for (i = 0; i < globbuf.gl_pathc; i++) { 479 if (globbuf.gl_pathv[i]) 480 if (conf->conf_file && 481 !strcmp(conf->conf_file, globbuf.gl_pathv[i])) 482 continue; 483 if (opkg_conf_parse_file(globbuf.gl_pathv[i], 484 &conf->pkg_src_list) < 0) { 485 globfree(&globbuf); 486 goto err1; 487 } 488 } 489 490 globfree(&globbuf); 491 492 if (conf->offline_root) 493 sprintf_alloc(&lock_file, "%s/%s", conf->offline_root, 494 OPKGLOCKFILE); 495 else 496 sprintf_alloc(&lock_file, "%s", OPKGLOCKFILE); 497 498 lock_fd = creat(lock_file, S_IRUSR | S_IWUSR | S_IRGRP); 499 if (lock_fd == -1) { 500 opkg_perror(ERROR, "Could not create lock file %s", lock_file); 501 goto err2; 502 } 503 504 if (lockf(lock_fd, F_TLOCK, (off_t) 0) == -1) { 505 opkg_perror(ERROR, "Could not lock %s", lock_file); 506 if (close(lock_fd) == -1) 507 opkg_perror(ERROR, "Couldn't close descriptor %d (%s)", 508 lock_fd, lock_file); 509 lock_fd = -1; 510 goto err2; 511 } 512 513 if (conf->tmp_dir) 514 tmp_dir_base = conf->tmp_dir; 515 else 516 tmp_dir_base = getenv("TMPDIR"); 517 518 sprintf_alloc(&tmp, "%s/%s", 519 tmp_dir_base ? tmp_dir_base : 520 OPKG_CONF_DEFAULT_TMP_DIR_BASE, OPKG_CONF_TMP_DIR_SUFFIX); 521 if (conf->tmp_dir) 522 free(conf->tmp_dir); 523 conf->tmp_dir = mkdtemp(tmp); 524 if (conf->tmp_dir == NULL) { 525 opkg_perror(ERROR, "Creating temp dir %s failed", tmp); 526 goto err3; 527 } 528 529 pkg_hash_init(); 530 hash_table_init("file-hash", &conf->file_hash, 531 OPKG_CONF_DEFAULT_HASH_LEN); 532 hash_table_init("obs-file-hash", &conf->obs_file_hash, 533 OPKG_CONF_DEFAULT_HASH_LEN / 16); 534 535 if (conf->lists_dir == NULL) 536 conf->lists_dir = xstrdup(OPKG_CONF_LISTS_DIR); 537 538 if (conf->verify_program == NULL) 539 conf->verify_program = xstrdup(OPKG_CONF_DEFAULT_VERIFY_PROGRAM); 540 541 if (conf->offline_root) { 542 sprintf_alloc(&tmp, "%s/%s", conf->offline_root, 543 conf->lists_dir); 544 free(conf->lists_dir); 545 conf->lists_dir = tmp; 546 } 547 548 /* if no architectures were defined, then default all, noarch, and host architecture */ 549 if (nv_pair_list_empty(&conf->arch_list)) { 550 nv_pair_list_append(&conf->arch_list, "all", "1"); 551 nv_pair_list_append(&conf->arch_list, "noarch", "1"); 552 nv_pair_list_append(&conf->arch_list, HOST_CPU_STR, "10"); 553 } 554 555 /* Even if there is no conf file, we'll need at least one dest. */ 556 if (nv_pair_list_empty(&conf->tmp_dest_list)) { 557 nv_pair_list_append(&conf->tmp_dest_list, 558 OPKG_CONF_DEFAULT_DEST_NAME, 559 OPKG_CONF_DEFAULT_DEST_ROOT_DIR); 560 } 561 562 if (resolve_pkg_dest_list()) 563 goto err4; 564 565 nv_pair_list_deinit(&conf->tmp_dest_list); 566 567 return 0; 568 569 err4: 570 free(conf->lists_dir); 571 572 pkg_hash_deinit(); 573 hash_table_deinit(&conf->file_hash); 574 hash_table_deinit(&conf->obs_file_hash); 575 576 if (rmdir(conf->tmp_dir) == -1) 577 opkg_perror(ERROR, "Couldn't remove dir %s", conf->tmp_dir); 578 err3: 579 if (lockf(lock_fd, F_ULOCK, (off_t) 0) == -1) 580 opkg_perror(ERROR, "Couldn't unlock %s", lock_file); 581 582 if (close(lock_fd) == -1) 583 opkg_perror(ERROR, "Couldn't close descriptor %d (%s)", 584 lock_fd, lock_file); 585 if (unlink(lock_file) == -1) 586 opkg_perror(ERROR, "Couldn't unlink %s", lock_file); 587 err2: 588 if (lock_file) { 589 free(lock_file); 590 lock_file = NULL; 591 } 592 err1: 593 pkg_src_list_deinit(&conf->pkg_src_list); 594 pkg_dest_list_deinit(&conf->pkg_dest_list); 595 nv_pair_list_deinit(&conf->arch_list); 596 597 for (i = 0; options[i].name; i++) { 598 if (options[i].type == OPKG_OPT_TYPE_STRING) { 599 tmp_val = (char **)options[i].value; 600 if (*tmp_val) { 601 free(*tmp_val); 602 *tmp_val = NULL; 603 } 604 } 605 } 606 err0: 607 nv_pair_list_deinit(&conf->tmp_dest_list); 608 if (conf->dest_str) 609 free(conf->dest_str); 610 if (conf->conf_file) 611 free(conf->conf_file); 612 613 return -1; 614 } 615 616 void opkg_conf_deinit(void) 617 { 618 int i; 619 char **tmp; 620 621 if (conf->tmp_dir) 622 rm_r(conf->tmp_dir); 623 624 if (conf->lists_dir) 625 free(conf->lists_dir); 626 627 if (conf->dest_str) 628 free(conf->dest_str); 629 630 if (conf->conf_file) 631 free(conf->conf_file); 632 633 pkg_src_list_deinit(&conf->pkg_src_list); 634 pkg_dest_list_deinit(&conf->pkg_dest_list); 635 nv_pair_list_deinit(&conf->arch_list); 636 637 for (i = 0; options[i].name; i++) { 638 if (options[i].type == OPKG_OPT_TYPE_STRING) { 639 tmp = (char **)options[i].value; 640 if (*tmp) { 641 free(*tmp); 642 *tmp = NULL; 643 } 644 } 645 } 646 647 if (conf->verbosity >= DEBUG) { 648 hash_print_stats(&conf->pkg_hash); 649 hash_print_stats(&conf->file_hash); 650 hash_print_stats(&conf->obs_file_hash); 651 } 652 653 pkg_hash_deinit(); 654 hash_table_deinit(&conf->file_hash); 655 hash_table_deinit(&conf->obs_file_hash); 656 657 if (lock_fd != -1) { 658 if (lockf(lock_fd, F_ULOCK, (off_t) 0) == -1) 659 opkg_perror(ERROR, "Couldn't unlock %s", lock_file); 660 661 if (close(lock_fd) == -1) 662 opkg_perror(ERROR, "Couldn't close descriptor %d (%s)", 663 lock_fd, lock_file); 664 665 } 666 667 if (lock_file) { 668 if (unlink(lock_file) == -1) 669 opkg_perror(ERROR, "Couldn't unlink %s", lock_file); 670 671 free(lock_file); 672 } 673 } 674
This page was automatically generated by LXR 0.3.1. • OpenWrt