triggers.c
1 /*- 2 * Copyright (c) 2020-2026 Baptiste Daroussin <bapt@FreeBSD.org> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include "pkg_config.h" 8 9 #if __has_include(<sys/capsicum.h>) 10 #include <sys/capsicum.h> 11 #define HAVE_CAPSICUM 1 12 #endif 13 14 #include <sys/stat.h> 15 #include <sys/wait.h> 16 17 #include <dirent.h> 18 #include <err.h> 19 #include <errno.h> 20 #include <fcntl.h> 21 #include <paths.h> 22 #include <spawn.h> 23 #include <xstring.h> 24 25 #include <pkg.h> 26 #include <private/pkg.h> 27 #include <private/event.h> 28 #include <private/lua.h> 29 30 extern char **environ; 31 32 static const unsigned char litchar[] = 33 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 34 35 static script_type_t 36 get_script_type(const char *str) 37 { 38 if (STRIEQ(str, "lua")) 39 return (SCRIPT_LUA); 40 return (SCRIPT_UNKNOWN); 41 } 42 43 static ucl_object_t * 44 trigger_open_schema(void) 45 { 46 struct ucl_parser *parser; 47 ucl_object_t *trigger_schema; 48 static const char trigger_schema_str[] = "" 49 "{" 50 " type = object;" 51 " properties {" 52 " description: { type = string };" 53 " path: { " 54 " anyOf = [{" 55 " type = array; " 56 " item = { type = string };" 57 " }, {" 58 " type = string;" 59 " }]" 60 " };" 61 " path_glob: { " 62 " anyOf = [{" 63 " type = array; " 64 " item = { type = string };" 65 " }, {" 66 " type = string;" 67 " }]" 68 " };" 69 " path_regexp: { " 70 " anyOf = [{" 71 " type = array; " 72 " item = { type = string };" 73 " }, {" 74 " type = string;" 75 " }]" 76 " };" 77 " cleanup = { " 78 " type = object; " 79 " properties = {" 80 " type = { " 81 " type = string," 82 " sandbox = boolean, " 83 " enum: [lua];" 84 " };" 85 " script = { type = string };" 86 " }; " 87 " required = [ type, script ];" 88 " };" 89 " trigger = { " 90 " type = object; " 91 " properties = {" 92 " type = { " 93 " type = string," 94 " sandbox = boolean, " 95 " enum: [lua];" 96 " };" 97 " script = { type = string };" 98 " }; " 99 " required = [ type, script ];" 100 " };" 101 " }\n" 102 " required = [ trigger ];" 103 "}"; 104 105 parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS); 106 if (!ucl_parser_add_chunk(parser, trigger_schema_str, 107 sizeof(trigger_schema_str) -1)) { 108 pkg_emit_error("Cannot parse schema for trigger: %s", 109 ucl_parser_get_error(parser)); 110 ucl_parser_free(parser); 111 return (NULL); 112 } 113 114 trigger_schema = ucl_parser_get_object(parser); 115 ucl_parser_free(parser); 116 return (trigger_schema); 117 } 118 119 static bool 120 parse_trigger_script_block(const ucl_object_t *block, const char *block_name, 121 const char *trigger_name, char **out_script, int *out_type, bool *out_sandbox) 122 { 123 const ucl_object_t *o; 124 125 o = ucl_object_find_key(block, "type"); 126 if (o == NULL) { 127 pkg_emit_error("%s %s doesn't have a script type", block_name, trigger_name); 128 return (false); 129 } 130 *out_type = get_script_type(ucl_object_tostring(o)); 131 if (*out_type == SCRIPT_UNKNOWN) { 132 pkg_emit_error("Unknown script type for %s in %s", block_name, trigger_name); 133 return (false); 134 } 135 o = ucl_object_find_key(block, "script"); 136 if (o == NULL) { 137 pkg_emit_error("No script in %s %s", block_name, trigger_name); 138 return (false); 139 } 140 *out_script = xstrdup(ucl_object_tostring(o)); 141 o = ucl_object_find_key(block, "sandbox"); 142 *out_sandbox = (o == NULL) ? true : ucl_object_toboolean(o); 143 return (true); 144 } 145 146 static struct trigger * 147 trigger_load(int dfd, const char *name, bool cleanup_only, ucl_object_t *schema) 148 { 149 struct ucl_parser *p; 150 ucl_object_t *obj = NULL; 151 const ucl_object_t *block = NULL; 152 int fd; 153 struct ucl_schema_error err; 154 struct trigger *t; 155 156 fd = openat(dfd, name, O_RDONLY); 157 if (fd == -1) { 158 pkg_emit_error("Unable to open the trigger: %s", name); 159 return (NULL); 160 } 161 162 p = ucl_parser_new(0); 163 if (!ucl_parser_add_fd(p, fd)) { 164 pkg_emit_error("Error parsing trigger '%s': %s", name, 165 ucl_parser_get_error(p)); 166 ucl_parser_free(p); 167 close(fd); 168 return (NULL); 169 } 170 close(fd); 171 172 obj = ucl_parser_get_object(p); 173 ucl_parser_free(p); 174 if (obj == NULL) 175 return (NULL); 176 177 if (!ucl_object_validate(schema, obj, &err)) { 178 pkg_emit_error("trigger definition %s cannot be validated: %s", name, err.msg); 179 ucl_object_unref(obj); 180 return (NULL); 181 } 182 183 t = xcalloc(1, sizeof(*t)); 184 t->name = xstrdup(name); 185 186 if (cleanup_only) { 187 block = ucl_object_find_key(obj, "cleanup"); 188 if (block == NULL) 189 goto err; 190 if (!parse_trigger_script_block(block, "cleanup", name, 191 &t->cleanup.script, &t->cleanup.type, &t->cleanup.sandbox)) 192 goto err; 193 ucl_object_unref(obj); 194 return (t); 195 } 196 197 block = ucl_object_find_key(obj, "trigger"); 198 if (!parse_trigger_script_block(block, "trigger", name, 199 &t->script.script, &t->script.type, &t->script.sandbox)) 200 goto err; 201 202 /* Load path patterns (required for any non-cleanup block) */ 203 block = ucl_object_find_key(obj, "path"); 204 if (block != NULL) 205 t->path = ucl_object_ref(block); 206 block = ucl_object_find_key(obj, "path_glob"); 207 if (block != NULL) 208 t->path_glob = ucl_object_ref(block); 209 block = ucl_object_find_key(obj, "path_regexp"); 210 if (block != NULL) 211 t->path_regexp = ucl_object_ref(block); 212 if (t->path == NULL && 213 t->path_glob == NULL && 214 t->path_regexp == NULL) { 215 pkg_emit_error("No path* in trigger %s, skipping", name); 216 goto err; 217 } 218 219 ucl_object_unref(obj); 220 return (t); 221 222 err: 223 if (t) { 224 if (t->path != NULL) 225 ucl_object_unref(t->path); 226 if (t->path_glob != NULL) 227 ucl_object_unref(t->path_glob); 228 if (t->path_regexp != NULL) 229 ucl_object_unref(t->path_regexp); 230 free(t->script.script); 231 free(t->cleanup.script); 232 free(t); 233 } 234 ucl_object_unref(obj); 235 return (NULL); 236 } 237 238 void 239 trigger_is_it_a_cleanup(struct triggers *t, const char *path) 240 { 241 const char *trigger_name, *dir; 242 const pkg_object *dirs, *cur; 243 struct trigger *trig; 244 pkg_iter it; 245 246 if (t->schema == NULL) 247 t->schema = trigger_open_schema(); 248 249 /* Check if the file was installed in a trigger directory. */ 250 it = NULL; 251 trigger_name = NULL; 252 dirs = pkg_config_get("PKG_TRIGGERS_DIR"); 253 while ((cur = pkg_object_iterate(dirs, &it))) { 254 size_t len; 255 256 dir = pkg_object_string(cur); 257 len = strlen(dir); 258 259 if (strncmp(path, dir, len) == 0) { 260 trigger_name = path + strlen(dir); 261 break; 262 } 263 } 264 265 if (trigger_name == NULL) 266 return; 267 268 if (t->dfd == -1) 269 t->dfd = openat(ctx.rootfd, RELATIVE_PATH(dir), O_DIRECTORY); 270 271 trig = trigger_load(t->dfd, RELATIVE_PATH(trigger_name), true, t->schema); 272 if (trig != NULL) { 273 if (t->cleanup == NULL) 274 t->cleanup = xcalloc(1, sizeof(*t->cleanup)); 275 276 vec_push(t->cleanup, trig); 277 } 278 } 279 280 /* 281 * Load triggers from a specific directory and add them to a vec. 282 */ 283 static void 284 triggers_load_from(trigger_t *triggers, bool cleanup_only, const char *dir) 285 { 286 int dfd; 287 DIR *d; 288 struct dirent *e; 289 struct trigger *t; 290 ucl_object_t *schema; 291 struct stat st; 292 293 dfd = openat(ctx.rootfd, dir, O_DIRECTORY); 294 if (dfd == -1) { 295 if (errno != ENOENT) 296 pkg_emit_error("Unable to open the trigger directory %s: %s", 297 dir, strerror(errno)); 298 return; 299 } 300 301 d = fdopendir(dfd); 302 if (d == NULL) { 303 pkg_emit_error("Unable to open the trigger directory %s: %s", 304 dir, strerror(errno)); 305 close(dfd); 306 return; 307 } 308 309 schema = trigger_open_schema(); 310 311 while ((e = readdir(d)) != NULL) { 312 const char *ext; 313 /* ignore all hidden files */ 314 if (e->d_name[0] == '.') 315 continue; 316 /* only consider files ending with .ucl */ 317 ext = strrchr(e->d_name, '.'); 318 if (ext == NULL) 319 continue; 320 if (!STREQ(ext, ".ucl")) 321 continue; 322 /* only regular files are considered */ 323 if (fstatat(dfd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) { 324 pkg_emit_errno("fstatat", e->d_name); 325 continue; 326 } 327 if (!S_ISREG(st.st_mode)) 328 continue; 329 t = trigger_load(dfd, e->d_name, cleanup_only, schema); 330 if (t != NULL) 331 vec_push(triggers, t); 332 } 333 334 closedir(d); 335 ucl_object_unref(schema); 336 } 337 338 /* 339 * Load triggers from PKG_TRIGGERS_DIR. 340 */ 341 trigger_t * 342 triggers_load(bool cleanup_only) 343 { 344 trigger_t *ret; 345 const pkg_object *dirs, *cur; 346 pkg_iter it = NULL; 347 348 ret = xcalloc(1, sizeof(*ret)); 349 350 dirs = pkg_config_get("PKG_TRIGGERS_DIR"); 351 while ((cur = pkg_object_iterate(dirs, &it))) { 352 const char *dir; 353 354 dir = RELATIVE_PATH(pkg_object_string(cur)); 355 triggers_load_from(ret, cleanup_only, dir); 356 } 357 358 return (ret); 359 } 360 361 void 362 trigger_free(struct trigger *t) 363 { 364 if (!t) 365 return; 366 free(t->name); 367 if (t->path) 368 ucl_object_unref(t->path); 369 if (t->path_glob) 370 ucl_object_unref(t->path_glob); 371 if (t->path_regexp) 372 ucl_object_unref(t->path_regexp); 373 free(t->cleanup.script); 374 free(t->script.script); 375 free(t); 376 } 377 378 static char * 379 get_random_name(char name[]) 380 { 381 char *pos; 382 int r; 383 384 pos = name; 385 while (*pos == 'X') { 386 #ifndef HAVE_ARC4RANDOM 387 r = rand() % (sizeof(litchar) -1); 388 #else 389 r = arc4random_uniform(sizeof(litchar) -1); 390 #endif 391 *pos++ = litchar[r]; 392 } 393 394 return (name); 395 } 396 397 static void 398 save_trigger(const char *script, bool sandbox, pkghash *args) 399 { 400 int db = ctx.pkg_dbdirfd; 401 pkghash_it it; 402 403 if (!mkdirat_p(db, "triggers")) 404 return; 405 406 int trigfd = openat(db, "triggers", O_DIRECTORY); 407 close(db); 408 if (trigfd == -1) { 409 pkg_errno("Failed to open '%s' as a directory", "triggers"); 410 return; 411 } 412 413 #ifndef HAVE_ARC4RANDOM 414 srand(time(NULL)); 415 #endif 416 417 int fd; 418 for (;;) { 419 char name[] = "XXXXXXXXXX"; 420 fd = openat(trigfd, get_random_name(name), 421 O_CREAT|O_RDWR|O_EXCL, 0644); 422 if (fd != -1) 423 break; 424 if (errno == EEXIST) 425 continue; 426 pkg_errno("Can't create deferred triggers %s", name); 427 return; 428 } 429 close(trigfd); 430 FILE *f = fdopen(fd, "w"); 431 if (sandbox) 432 fputs("--sandbox\n", f); 433 fputs("--begin args\n", f); 434 it = pkghash_iterator(args); 435 while (pkghash_next(&it)) { 436 fprintf(f, "-- %s\n", (char *)it.value); 437 } 438 fputs("--end args\n--\n", f); 439 fprintf(f, "%s\n", script); 440 fclose(f); 441 } 442 443 static int 444 trigger_execute_lua_common(const char *script, bool sandbox, pkghash *args, 445 bool defer, const char *pkgname, const char *pkgversion, bool upgrade) 446 { 447 lua_State *L; 448 int pstat; 449 pkghash_it it; 450 451 if (defer && !sandbox && ctx.defer_triggers) { 452 save_trigger(script, sandbox, args); 453 return (EPKG_OK); 454 } 455 456 pid_t pid = fork(); 457 if (pid == 0) { 458 L = luaL_newstate(); 459 luaL_openlibs(L); 460 lua_override_ios(L, sandbox); 461 static const luaL_Reg pkg_lib[] = { 462 { "print_msg", lua_print_msg }, 463 { "filecmp", lua_pkg_filecmp }, 464 { "copy", lua_pkg_copy }, 465 { "stat", lua_stat }, 466 { "readdir", lua_readdir }, 467 { "exec", lua_exec }, 468 { "symlink", lua_pkg_symlink }, 469 { NULL, NULL }, 470 }; 471 luaL_newlib(L, pkg_lib); 472 lua_setglobal(L, "pkg"); 473 lua_pushinteger(L, ctx.rootfd); 474 lua_setglobal(L, "rootfd"); 475 if (pkgname != NULL) { 476 lua_pushstring(L, pkgname); 477 lua_setglobal(L, "pkg_name"); 478 } 479 if (pkgversion != NULL) { 480 lua_pushstring(L, pkgversion); 481 lua_setglobal(L, "pkg_version"); 482 } 483 lua_pushboolean(L, upgrade); 484 lua_setglobal(L, "pkg_upgrade"); 485 char **arguments = NULL; 486 int i = 0; 487 if (args != NULL) { 488 arguments = xcalloc(pkghash_count(args), sizeof(char*)); 489 it = pkghash_iterator(args); 490 while (pkghash_next(&it)) { 491 arguments[i++] = it.key; 492 } 493 } 494 lua_args_table(L, arguments, i); 495 #ifdef HAVE_CAPSICUM 496 if (sandbox) { 497 #ifndef COVERAGE 498 if (cap_enter() < 0 && errno != ENOSYS) { 499 err(1, "cap_enter failed"); 500 } 501 #endif 502 } 503 #endif 504 if (luaL_dostring(L, script)) { 505 pkg_emit_error("Failed to execute lua trigger: " 506 "%s", lua_tostring(L, -1)); 507 _exit(1); 508 } 509 if (lua_tonumber(L, -1) != 0) { 510 lua_close(L); 511 _exit(1); 512 } 513 lua_close(L); 514 _exit(0); 515 } else if (pid < 0) { 516 pkg_emit_errno("Cannot fork", "lua_script"); 517 return (EPKG_FATAL); 518 } 519 while (waitpid(pid, &pstat, 0) == -1) { 520 if (errno != EINTR) { 521 pkg_emit_error("waitpid() failed: %s", strerror(errno)); 522 return (EPKG_FATAL ); 523 } 524 } 525 if (WEXITSTATUS(pstat) != 0) { 526 pkg_emit_error("lua script failed"); 527 return (EPKG_FATAL); 528 } 529 530 return (EPKG_OK); 531 } 532 533 static int 534 trigger_execute_lua(const char *script, bool sandbox, pkghash *args) 535 { 536 return (trigger_execute_lua_common(script, sandbox, args, 537 true, NULL, NULL, false)); 538 } 539 540 static void 541 trigger_check_match(struct trigger *t, char *dir) 542 { 543 const ucl_object_t *cur; 544 ucl_object_iter_t it; 545 546 if (t->path != NULL) { 547 it = NULL; 548 while ((cur = ucl_iterate_object(t->path, &it, true))) { 549 if (STREQ(dir, ucl_object_tostring(cur))) { 550 pkghash_safe_add(t->matched, dir, dir, NULL); 551 return; 552 } 553 } 554 } 555 556 if (match_ucl_lists(dir, t->path_glob, t->path_regexp)) { 557 pkghash_safe_add(t->matched, dir, dir, NULL); 558 } 559 } 560 561 /* 562 * first execute cleanup scripts from the triggers that are not there anymore, 563 * then execute all per-transaction triggers that matched touched directories. 564 * Always reload triggers from disk: new trigger files may have been installed 565 * during the transaction. 566 */ 567 int 568 triggers_execute(struct triggers *t) 569 { 570 trigger_t *triggers; 571 int ret = EPKG_OK; 572 573 triggers = triggers_load(false); 574 575 pkg_emit_triggers_begin(); 576 if (t != NULL && t->cleanup != NULL) { 577 vec_foreach(*t->cleanup, i) { 578 pkg_emit_trigger(t->cleanup->d[i]->name, true); 579 if (t->cleanup->d[i]->cleanup.type == SCRIPT_LUA) { 580 ret = trigger_execute_lua(t->cleanup->d[i]->cleanup.script, 581 t->cleanup->d[i]->cleanup.sandbox, NULL); 582 } 583 if (ret != EPKG_OK) 584 goto cleanup; 585 } 586 } 587 588 if (ctx.touched_dir_hash) { 589 pkghash_it it = pkghash_iterator(ctx.touched_dir_hash); 590 while (pkghash_next(&it)) { 591 vec_foreach(*triggers, i) 592 trigger_check_match(triggers->d[i], it.key); 593 } 594 } 595 596 vec_foreach(*triggers, i) { 597 if (triggers->d[i]->matched == NULL) 598 continue; 599 pkg_emit_trigger(triggers->d[i]->name, false); 600 if (triggers->d[i]->script.type == SCRIPT_LUA) { 601 ret = trigger_execute_lua(triggers->d[i]->script.script, 602 triggers->d[i]->script.sandbox, triggers->d[i]->matched); 603 } 604 if (ret != EPKG_OK) 605 goto cleanup; 606 } 607 pkg_emit_triggers_finished(); 608 609 cleanup: 610 vec_free_and_free(triggers, trigger_free); 611 free(triggers); 612 613 return (EPKG_OK); 614 } 615 616 /* 617 * Match a dir against a trigger's path patterns, adding to local_matched. 618 */ 619 static bool 620 trigger_check_match_local(struct trigger *t, const char *dir, pkghash **matched) 621 { 622 const ucl_object_t *cur; 623 ucl_object_iter_t it; 624 625 if (t->path != NULL) { 626 it = NULL; 627 while ((cur = ucl_iterate_object(t->path, &it, true))) { 628 if (STREQ(dir, ucl_object_tostring(cur))) { 629 pkghash_safe_add(*matched, dir, NULL, NULL); 630 return (true); 631 } 632 } 633 } 634 635 if (match_ucl_lists(dir, t->path_glob, t->path_regexp)) { 636 pkghash_safe_add(*matched, dir, NULL, NULL); 637 return (true); 638 } 639 return (false); 640 } 641 642 /* 643 * Per-package trigger subdirectory names, indexed by trigger_phase_t. 644 */ 645 static const char *trigger_phase_dirs[] = { 646 [TRIGGER_PHASE_PRE_INSTALL] = "pre_install", 647 [TRIGGER_PHASE_POST_INSTALL] = "post_install", 648 [TRIGGER_PHASE_PRE_DEINSTALL] = "pre_deinstall", 649 [TRIGGER_PHASE_POST_DEINSTALL] = "post_deinstall", 650 }; 651 652 /* 653 * Load per-package triggers from the phase-specific subdirectory 654 * under each configured PKG_TRIGGERS_DIR. 655 */ 656 static trigger_t * 657 triggers_load_perpackage(trigger_phase_t phase) 658 { 659 trigger_t *ret; 660 const pkg_object *dirs, *cur; 661 pkg_iter it = NULL; 662 char path[MAXPATHLEN]; 663 664 ret = xcalloc(1, sizeof(*ret)); 665 666 dirs = pkg_config_get("PKG_TRIGGERS_DIR"); 667 while ((cur = pkg_object_iterate(dirs, &it))) { 668 const char *dir; 669 670 dir = RELATIVE_PATH(pkg_object_string(cur)); 671 snprintf(path, sizeof(path), "%s/%s", 672 dir, trigger_phase_dirs[phase]); 673 triggers_load_from(ret, false, path); 674 } 675 676 return (ret); 677 } 678 679 /* 680 * Execute per-package triggers for a given phase on a specific package. 681 * Triggers are loaded from the phase-specific subdirectory (e.g. 682 * post_install/) so only relevant triggers are parsed. They are 683 * reloaded from disk each time so that triggers installed or removed 684 * by earlier packages in the same transaction are picked up. 685 */ 686 int 687 triggers_execute_perpackage(struct triggers *t, struct pkg *pkg, 688 trigger_phase_t phase, bool upgrade) 689 { 690 struct pkg_file *f = NULL; 691 struct pkg_dir *d = NULL; 692 pkghash *pkg_dirs_hash = NULL; 693 int ret = EPKG_OK; 694 trigger_t *triggers; 695 696 if (t == NULL) 697 return (EPKG_OK); 698 699 triggers = triggers_load_perpackage(phase); 700 701 if (triggers->len == 0) { 702 vec_free_and_free(triggers, trigger_free); 703 free(triggers); 704 return (EPKG_OK); 705 } 706 707 /* Build set of parent directories from the package's files */ 708 while (pkg_files(pkg, &f) == EPKG_OK) { 709 char *dir, *slash; 710 dir = xstrdup(f->path); 711 slash = strrchr(dir, '/'); 712 if (slash != NULL) { 713 *slash = '\0'; 714 pkghash_safe_add(pkg_dirs_hash, dir, NULL, NULL); 715 } 716 free(dir); 717 } 718 while (pkg_dirs(pkg, &d) == EPKG_OK) { 719 pkghash_safe_add(pkg_dirs_hash, d->path, NULL, NULL); 720 } 721 722 if (pkg_dirs_hash == NULL) { 723 vec_free_and_free(triggers, trigger_free); 724 free(triggers); 725 return (EPKG_OK); 726 } 727 728 /* Match and execute */ 729 vec_foreach(*triggers, i) { 730 struct trigger *trig = triggers->d[i]; 731 pkghash *local_matched = NULL; 732 pkghash_it it = pkghash_iterator(pkg_dirs_hash); 733 734 while (pkghash_next(&it)) 735 trigger_check_match_local(trig, it.key, &local_matched); 736 737 if (local_matched == NULL) 738 continue; 739 740 pkg_emit_trigger(trig->name, false); 741 if (trig->script.type == SCRIPT_LUA) { 742 ret = trigger_execute_lua_common(trig->script.script, 743 trig->script.sandbox, local_matched, false, 744 pkg->name, pkg->version, upgrade); 745 } 746 pkghash_destroy(local_matched); 747 if (ret != EPKG_OK) { 748 pkg_emit_error("per-package trigger %s failed " 749 "for %s, continuing", trig->name, pkg->name); 750 ret = EPKG_OK; 751 } 752 } 753 754 pkghash_destroy(pkg_dirs_hash); 755 vec_free_and_free(triggers, trigger_free); 756 free(triggers); 757 return (ret); 758 } 759 760 void 761 append_touched_dir(const char *path) 762 { 763 pkghash_safe_add(ctx.touched_dir_hash, path, NULL, NULL); 764 } 765 766 void 767 append_touched_file(const char *path) 768 { 769 char *newpath, *walk; 770 771 newpath = xstrdup(path); 772 walk = strrchr(newpath, '/'); 773 if (walk == NULL) 774 return; 775 *walk = '\0'; 776 777 pkghash_safe_add(ctx.touched_dir_hash, newpath, NULL, NULL ); 778 free(newpath); 779 } 780 781 static void 782 exec_deferred(int dfd, const char *name) 783 { 784 bool sandbox = false; 785 pkghash *args = NULL; 786 xstring *script = NULL; 787 788 int fd = openat(dfd, name, O_RDONLY); 789 if (fd == -1) { 790 pkg_errno("Unable to open the trigger '%s'", name); 791 return; 792 } 793 FILE *f = fdopen(fd, "r"); 794 if (f == NULL) { 795 pkg_errno("Unable to open the trigger '%s'", name); 796 return; 797 } 798 799 char *line = NULL; 800 size_t linecap = 0; 801 ssize_t linelen; 802 char *walk; 803 bool inargs = false; 804 while ((linelen = getline(&line, &linecap, f)) > 0) { 805 walk = line; 806 walk += 2; /* '--' aka lua comments */ 807 if (strncmp(walk, "sandbox", 7) == 0) { 808 sandbox = true; 809 continue; 810 } 811 if (strncmp(walk, "begin args", 10) == 0) { 812 inargs = true; 813 continue; 814 } 815 if (strncmp(walk, "end args", 8) == 0) { 816 inargs = false; 817 script = xstring_new(); 818 continue; 819 } 820 if (inargs) { 821 walk++; /* skip the space */ 822 if (line[linelen -1] == '\n') 823 line[linelen -1] = '\0'; 824 pkghash_safe_add(args, walk, NULL, NULL); 825 } 826 if (script != NULL) 827 fputs(line, script->fp); 828 } 829 free(line); 830 fclose(f); 831 if (script == NULL) { 832 pkghash_destroy(args); 833 return; 834 } 835 char *s = xstring_get(script); 836 if (trigger_execute_lua(s, sandbox, args) == EPKG_OK) { 837 unlinkat(dfd, name, 0); 838 } 839 free(s); 840 pkghash_destroy(args); 841 } 842 843 int 844 pkg_execute_deferred_triggers(void) 845 { 846 struct dirent *e; 847 struct stat st; 848 int dbdir = pkg_get_dbdirfd(); 849 850 int trigfd = openat(dbdir, "triggers", O_DIRECTORY); 851 if (trigfd == -1) 852 return (EPKG_OK); 853 854 DIR *d = fdopendir(trigfd); 855 if (d == NULL) { 856 close(trigfd); 857 pkg_emit_error("Unable to open the deferred trigger directory"); 858 return (EPKG_FATAL); 859 } 860 861 while ((e = readdir(d)) != NULL) { 862 /* ignore all hiddn files */ 863 if (e->d_name[0] == '.') 864 continue; 865 /* only regular files are considered */ 866 if (fstatat(trigfd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) { 867 pkg_emit_errno("fstatat", e->d_name); 868 return (EPKG_FATAL); 869 } 870 exec_deferred(trigfd, e->d_name); 871 } 872 closedir(d); 873 return (EPKG_OK); 874 }