Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2020-2022 Baptiste Daroussin <bapt@FreeBSD.org>
3 : : *
4 : : * Redistribution and use in source and binary forms, with or without
5 : : * modification, are permitted provided that the following conditions
6 : : * are met:
7 : : * 1. Redistributions of source code must retain the above copyright
8 : : * notice, this list of conditions and the following disclaimer
9 : : * in this position and unchanged.
10 : : * 2. Redistributions in binary form must reproduce the above copyright
11 : : * notice, this list of conditions and the following disclaimer in the
12 : : * documentation and/or other materials provided with the distribution.
13 : : *
14 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 : : */
25 : :
26 : : #include "pkg_config.h"
27 : :
28 : : #ifdef HAVE_CAPSICUM
29 : : #include <sys/capsicum.h>
30 : : #endif
31 : :
32 : : #include <sys/stat.h>
33 : : #include <sys/wait.h>
34 : :
35 : : #include <dirent.h>
36 : : #include <err.h>
37 : : #include <errno.h>
38 : : #include <fcntl.h>
39 : : #include <paths.h>
40 : : #include <spawn.h>
41 : : #include <xstring.h>
42 : :
43 : : #include <private/pkg.h>
44 : : #include <private/event.h>
45 : : #include <private/lua.h>
46 : :
47 : : extern char **environ;
48 : :
49 : : static const unsigned char litchar[] =
50 : : "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
51 : :
52 : : static script_type_t
53 : 1305 : get_script_type(const char *str)
54 : : {
55 [ + - ]: 1305 : if (STRIEQ(str, "lua"))
56 : 1305 : return (SCRIPT_LUA);
57 : 0 : return (SCRIPT_UNKNOWN);
58 : 1305 : }
59 : :
60 : : static ucl_object_t *
61 : 180 : trigger_open_schema(void)
62 : : {
63 : : struct ucl_parser *parser;
64 : : ucl_object_t *trigger_schema;
65 : : static const char trigger_schema_str[] = ""
66 : : "{"
67 : : " type = object;"
68 : : " properties {"
69 : : " description: { type = string };"
70 : : " path: { "
71 : : " anyOf = [{"
72 : : " type = array; "
73 : : " item = { type = string };"
74 : : " }, {"
75 : : " type = string;"
76 : : " }]"
77 : : " };"
78 : : " path_glob: { "
79 : : " anyOf = [{"
80 : : " type = array; "
81 : : " item = { type = string };"
82 : : " }, {"
83 : : " type = string;"
84 : : " }]"
85 : : " };"
86 : : " path_regexp: { "
87 : : " anyOf = [{"
88 : : " type = array; "
89 : : " item = { type = string };"
90 : : " }, {"
91 : : " type = string;"
92 : : " }]"
93 : : " };"
94 : : " cleanup = { "
95 : : " type = object; "
96 : : " properties = {"
97 : : " type = { "
98 : : " type = string,"
99 : : " sandbox = boolean, "
100 : : " enum: [lua];"
101 : : " };"
102 : : " script = { type = string };"
103 : : " }; "
104 : : " required = [ type, script ];"
105 : : " };"
106 : : " trigger = { "
107 : : " type = object; "
108 : : " properties = {"
109 : : " type = { "
110 : : " type = string,"
111 : : " sandbox = boolean, "
112 : : " enum: [lua];"
113 : : " };"
114 : : " script = { type = string };"
115 : : " }; "
116 : : " required = [ type, script ];"
117 : : " };"
118 : : " }\n"
119 : : " required = [ trigger ];"
120 : : "}";
121 : :
122 : 180 : parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
123 [ - + ]: 180 : if (!ucl_parser_add_chunk(parser, trigger_schema_str,
124 : : sizeof(trigger_schema_str) -1)) {
125 : 0 : pkg_emit_error("Cannot parse schema for trigger: %s",
126 : 0 : ucl_parser_get_error(parser));
127 : 0 : ucl_parser_free(parser);
128 : 0 : return (NULL);
129 : : }
130 : :
131 : 180 : trigger_schema = ucl_parser_get_object(parser);
132 : 180 : ucl_parser_free(parser);
133 : 180 : return (trigger_schema);
134 : 180 : }
135 : :
136 : : static struct trigger *
137 : 1305 : trigger_load(int dfd, const char *name, bool cleanup_only, ucl_object_t *schema)
138 : : {
139 : : struct ucl_parser *p;
140 : 1305 : ucl_object_t *obj = NULL;
141 : 1305 : const ucl_object_t *o = NULL, *trigger = NULL, *cleanup = NULL;
142 : : int fd;
143 : : struct ucl_schema_error err;
144 : : struct trigger *t;
145 : :
146 : 1305 : fd = openat(dfd, name, O_RDONLY);
147 [ - + ]: 1305 : if (fd == -1) {
148 : 0 : pkg_emit_error("Unable to open the tigger: %s", name);
149 : 0 : return (NULL);
150 : : }
151 : :
152 : 1305 : p = ucl_parser_new(0);
153 [ - + ]: 1305 : if (!ucl_parser_add_fd(p, fd)) {
154 : 0 : pkg_emit_error("Error parsing trigger '%s': %s", name,
155 : 0 : ucl_parser_get_error(p));
156 : 0 : ucl_parser_free(p);
157 : 0 : close(fd);
158 : 0 : return (NULL);
159 : : }
160 : 1305 : close(fd);
161 : :
162 : 1305 : obj = ucl_parser_get_object(p);
163 : 1305 : ucl_parser_free(p);
164 [ - + ]: 1305 : if (obj == NULL)
165 : 0 : return (NULL);
166 : :
167 [ - + ]: 1305 : if (!ucl_object_validate(schema, obj, &err)) {
168 : 0 : pkg_emit_error("trigger definition %s cannot be validated: %s", name, err.msg);
169 : 0 : ucl_object_unref(obj);
170 : 0 : return (NULL);
171 : : }
172 : :
173 : 1305 : t = xcalloc(1, sizeof(*t));
174 : 1305 : t->name = xstrdup(name);
175 : :
176 [ + + ]: 1305 : if (cleanup_only) {
177 : 1 : cleanup = ucl_object_find_key(obj, "cleanup");
178 [ - + ]: 1 : if (cleanup == NULL)
179 : 0 : goto err;
180 : 1 : o = ucl_object_find_key(cleanup, "type");
181 [ - + ]: 1 : if (o == NULL) {
182 : 0 : pkg_emit_error("cleanup %s doesn't have a script type", name);
183 : 0 : goto err;
184 : : }
185 : 1 : t->cleanup.type = get_script_type(ucl_object_tostring(o));
186 [ - + ]: 1 : if (t->cleanup.type == SCRIPT_UNKNOWN) {
187 : 0 : pkg_emit_error("Unknown script type for cleanup in %s", name);
188 : 0 : goto err;
189 : : }
190 : 1 : o = ucl_object_find_key(cleanup, "script");
191 [ - + ]: 1 : if (o == NULL) {
192 : 0 : pkg_emit_error("No script in cleanup %s", name);
193 : 0 : goto err;
194 : : }
195 : :
196 : 1 : t->cleanup.script = xstrdup(ucl_object_tostring(o));
197 : 1 : o = ucl_object_find_key(cleanup, "sandbox");
198 [ + - ]: 1 : if (o == NULL) {
199 : 1 : t->cleanup.sandbox = true;
200 : 1 : } else {
201 : 0 : t->cleanup.sandbox = ucl_object_toboolean(o);
202 : : }
203 : 1 : ucl_object_unref(obj);
204 : 1 : return (t);
205 : : }
206 : :
207 : 1304 : trigger = ucl_object_find_key(obj, "trigger");
208 [ - + ]: 1304 : if (trigger == NULL) {
209 : 0 : pkg_emit_error("trigger %s doesn't have any trigger block, ignoring", name);
210 : 0 : goto err;
211 : : }
212 : :
213 : 1304 : o = ucl_object_find_key(trigger, "type");
214 [ - + ]: 1304 : if (o == NULL) {
215 : 0 : pkg_emit_error("trigger %s doesn't have a script type", name);
216 : 0 : goto err;
217 : : }
218 : 1304 : t->script.type = get_script_type(ucl_object_tostring(o));
219 [ - + ]: 1304 : if (t->script.type == SCRIPT_UNKNOWN) {
220 : 0 : pkg_emit_error("Unknown script type for trigger in %s", name);
221 : 0 : goto err;
222 : : }
223 : 1304 : o = ucl_object_find_key(trigger, "script");
224 [ - + ]: 1304 : if (o == NULL) {
225 : 0 : pkg_emit_error("No script in trigger %s", name);
226 : 0 : goto err;
227 : : }
228 : 1304 : t->script.script = xstrdup(ucl_object_tostring(o));
229 : 1304 : o = ucl_object_find_key(trigger, "sandbox");
230 [ + + ]: 1304 : if (o == NULL) {
231 : 6 : t->script.sandbox = true;
232 : 6 : } else {
233 : 1298 : t->script.sandbox = ucl_object_toboolean(o);
234 : : }
235 : :
236 : 1304 : o = ucl_object_find_key(obj, "path");
237 : 1304 : if (o != NULL)
238 : 1010 : t->path = ucl_object_ref(o);
239 : 1727 : o = ucl_object_find_key(obj, "path_glob");
240 : 1727 : if (o != NULL)
241 : 293 : t->path_glob = ucl_object_ref(o);
242 : 585 : o = ucl_object_find_key(obj, "path_regex");
243 : 585 : if (o != NULL)
244 : 1 : t->path_regex = ucl_object_ref(o);
245 : 295 : if (t->path == NULL &&
246 : 294 : t->path_glob == NULL &&
247 : 1 : t->path_regex == NULL) {
248 : 0 : pkg_emit_error("No path* in trigger %s, skipping", name);
249 : 0 : goto err;
250 : : }
251 : :
252 : 1304 : ucl_object_unref(obj);
253 : 1304 : return (t);
254 : :
255 : : err:
256 : 0 : if (t) {
257 : 0 : if (t->path != NULL)
258 : 0 : ucl_object_unref(t->path);
259 : 0 : if (t->path_glob != NULL)
260 : 0 : ucl_object_unref(t->path_glob);
261 : 0 : if (t->path_regex != NULL)
262 : 0 : ucl_object_unref(t->path_regex);
263 : 0 : if (t->script.script != NULL)
264 : 0 : free(t->script.script);
265 : 0 : if (t->cleanup.script != NULL)
266 : 0 : free(t->cleanup.script);
267 : 0 : free(t);
268 : 0 : }
269 : 0 : ucl_object_unref(obj);
270 : 0 : return (NULL);
271 : 1305 : }
272 : :
273 : : void
274 : 44 : trigger_is_it_a_cleanup(struct triggers *t, const char *path)
275 : : {
276 : : const char *trigger_name;
277 : : struct trigger *trig;
278 : :
279 : 44 : if (t->schema == NULL)
280 : 27 : t->schema = trigger_open_schema();
281 [ + + ]: 44 : if (strncmp(path, ctx.triggers_path, strlen(ctx.triggers_path)) != 0)
282 : 43 : return;
283 : :
284 : 1 : trigger_name = path + strlen(ctx.triggers_path);
285 : :
286 : 1 : if (t->dfd == -1)
287 : 1 : t->dfd = openat(ctx.rootfd, RELATIVE_PATH(ctx.triggers_path), O_DIRECTORY);
288 : 1 : trig = trigger_load(t->dfd, RELATIVE_PATH(trigger_name), true, t->schema);
289 : 1 : if (trig != NULL) {
290 : 1 : if (t->cleanup == NULL)
291 : 1 : t->cleanup = xcalloc(1, sizeof(*t->cleanup));
292 : :
293 [ - + ]: 1 : tll_push_back(*t->cleanup, trig);
294 : 1 : }
295 : 44 : }
296 : :
297 : : trigger_t *
298 : 228 : triggers_load(bool cleanup_only)
299 : : {
300 : : int dfd;
301 : : DIR *d;
302 : : struct dirent *e;
303 : : struct trigger *t;
304 : 228 : trigger_t *triggers = xcalloc(1, sizeof(*triggers));
305 : : ucl_object_t *schema;
306 : : struct stat st;
307 : :
308 : 228 : dfd = openat(ctx.rootfd, RELATIVE_PATH(ctx.triggers_path), O_DIRECTORY);
309 [ + + ]: 228 : if (dfd == -1) {
310 : 75 : if (errno != ENOENT)
311 : 0 : pkg_emit_error("Unable to open the trigger directory");
312 : 75 : return (triggers);
313 : : }
314 : 153 : d = fdopendir(dfd);
315 [ - + ]: 153 : if (d == NULL) {
316 : 0 : pkg_emit_error("Unable to open the trigger directory");
317 : 0 : close(dfd);
318 : 0 : return (triggers);
319 : : }
320 : :
321 : 153 : schema = trigger_open_schema();
322 : :
323 [ + + ]: 1763 : while ((e = readdir(d)) != NULL) {
324 : : const char *ext;
325 : : /* ignore all hidden files */
326 [ + + ]: 1610 : if (e->d_name[0] == '.')
327 : 306 : continue;
328 : : /* only consider files ending with .ucl */
329 : 1304 : ext = strrchr(e->d_name, '.');
330 [ - + ]: 1304 : if (ext == NULL)
331 : 0 : continue;
332 [ - + ]: 1304 : if (!STREQ(ext, ".ucl"))
333 : 0 : continue;
334 : : /* only regular files are considered */
335 [ - + ]: 1304 : if (fstatat(dfd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) {
336 : 0 : pkg_emit_errno("fstatat", e->d_name);
337 : 0 : return (triggers);
338 : : }
339 [ - + ]: 1304 : if (!S_ISREG(st.st_mode))
340 : 0 : continue;
341 : 1304 : t = trigger_load(dfd, e->d_name, cleanup_only, schema);
342 : 1304 : if (t != NULL)
343 [ + + ]: 1304 : tll_push_back(*triggers, t);
344 : : }
345 : :
346 : 153 : closedir(d);
347 : 153 : ucl_object_unref(schema);
348 : 153 : return (triggers);
349 : 228 : }
350 : :
351 : : void
352 : 1305 : trigger_free(struct trigger *t)
353 : : {
354 [ - + ]: 1305 : if (!t)
355 : 0 : return;
356 : 1305 : free(t->name);
357 : 1305 : if (t->path)
358 : 1010 : ucl_object_unref(t->path);
359 : 1727 : if (t->path_glob)
360 : 293 : ucl_object_unref(t->path_glob);
361 : 585 : if (t->path_regex)
362 : 1 : ucl_object_unref(t->path_regex);
363 : 1305 : free(t->cleanup.script);
364 : 1305 : free(t->script.script);
365 : 1305 : free(t);
366 : 1305 : }
367 : :
368 : : static char *
369 : 1 : get_random_name(char name[])
370 : : {
371 : : char *pos;
372 : : int r;
373 : :
374 : 1 : pos = name;
375 [ + + ]: 11 : while (*pos == 'X') {
376 : : #ifndef HAVE_ARC4RANDOM
377 : : r = rand() % (sizeof(litchar) -1);
378 : : #else
379 : 10 : r = arc4random_uniform(sizeof(litchar) -1);
380 : : #endif
381 : 10 : *pos++ = litchar[r];
382 : : }
383 : :
384 : 1 : return (name);
385 : : }
386 : :
387 : : static void
388 : 1 : save_trigger(const char *script, bool sandbox, pkghash *args)
389 : : {
390 : 1 : int db = ctx.pkg_dbdirfd;
391 : : pkghash_it it;
392 : :
393 [ - + ]: 1 : if (!mkdirat_p(db, "triggers"))
394 : 0 : return;
395 : :
396 : 1 : int trigfd = openat(db, "triggers", O_DIRECTORY);
397 : 1 : close(db);
398 [ - + ]: 1 : if (trigfd == -1) {
399 : 0 : pkg_errno("Failed to open '%s' as a directory", "triggers");
400 : 0 : return;
401 : : }
402 : :
403 : : #ifndef HAVE_ARC4RANDOM
404 : : srand(time(NULL));
405 : : #endif
406 : :
407 : : int fd;
408 : 1 : for (;;) {
409 : 1 : char name[] = "XXXXXXXXXX";
410 : 1 : fd = openat(trigfd, get_random_name(name),
411 : : O_CREAT|O_RDWR|O_EXCL, 0644);
412 [ + - ]: 1 : if (fd != -1)
413 : 1 : break;
414 [ # # ]: 0 : if (errno == EEXIST)
415 : 0 : continue;
416 : 0 : pkg_errno("Can't create deferred triggers %s", name);
417 : 0 : return;
418 : : }
419 : 1 : close(trigfd);
420 : 1 : FILE *f = fdopen(fd, "w");
421 : 1 : if (sandbox)
422 : 1 : fputs("--sandbox\n", f);
423 : 1 : fputs("--begin args\n", f);
424 : 1 : it = pkghash_iterator(args);
425 [ + + ]: 2 : while (pkghash_next(&it)) {
426 : 1 : fprintf(f, "-- %s\n", (char *)it.value);
427 : : }
428 : 1 : fputs("--end args\n--\n", f);
429 : 1 : fprintf(f, "%s\n", script);
430 : 1 : fclose(f);
431 : 1 : }
432 : :
433 : : static int
434 : 9 : trigger_execute_lua(const char *script, bool sandbox, pkghash *args)
435 : : {
436 : : lua_State *L;
437 : : int pstat;
438 : : pkghash_it it;
439 : :
440 [ + + ]: 9 : if (ctx.defer_triggers) {
441 : 1 : save_trigger(script, sandbox, args);
442 : 1 : return (EPKG_OK);
443 : : }
444 : :
445 : 8 : pid_t pid = fork();
446 [ - + ]: 8 : if (pid == 0) {
447 : 0 : L = luaL_newstate();
448 : 0 : luaL_openlibs(L);
449 : 0 : lua_override_ios(L, sandbox);
450 : : static const luaL_Reg pkg_lib[] = {
451 : : { "print_msg", lua_print_msg },
452 : : { "filecmp", lua_pkg_filecmp },
453 : : { "copy", lua_pkg_copy },
454 : : { "stat", lua_stat },
455 : : { "readdir", lua_readdir },
456 : : { "exec", lua_exec },
457 : : { "symlink", lua_pkg_symlink },
458 : : { NULL, NULL },
459 : : };
460 : 0 : luaL_newlib(L, pkg_lib);
461 : 0 : lua_setglobal(L, "pkg");
462 : 0 : lua_pushinteger(L, ctx.rootfd);
463 : 0 : lua_setglobal(L, "rootfd");
464 : 0 : char **arguments = NULL;
465 : 0 : int i = 0;
466 : 0 : if (args != NULL) {
467 : 0 : arguments = xcalloc(pkghash_count(args), sizeof(char*));
468 : 0 : it = pkghash_iterator(args);
469 [ # # ]: 0 : while (pkghash_next(&it)) {
470 : 0 : arguments[i++] = it.key;
471 : : }
472 : 0 : }
473 : 0 : lua_args_table(L, arguments, i);
474 : : #ifdef HAVE_CAPSICUM
475 : : if (sandbox) {
476 : : #ifndef PKG_COVERAGE
477 : : if (cap_enter() < 0 && errno != ENOSYS) {
478 : : err(1, "cap_enter failed");
479 : : }
480 : : #endif
481 : : }
482 : : #endif
483 : 0 : if (luaL_dostring(L, script)) {
484 : 0 : pkg_emit_error("Failed to execute lua trigger: "
485 : 0 : "%s", lua_tostring(L, -1));
486 : 0 : _exit(1);
487 : : }
488 [ # # ]: 0 : if (lua_tonumber(L, -1) != 0) {
489 : 0 : lua_close(L);
490 : 0 : _exit(1);
491 : : }
492 : 0 : lua_close(L);
493 : 0 : _exit(0);
494 [ - + ]: 8 : } else if (pid < 0) {
495 : 0 : pkg_emit_errno("Cannot fork", "lua_script");
496 : 0 : return (EPKG_FATAL);
497 : : }
498 [ - + ]: 8 : while (waitpid(pid, &pstat, 0) == -1) {
499 [ # # ]: 0 : if (errno != EINTR) {
500 : 0 : pkg_emit_error("waitpid() failed: %s", strerror(errno));
501 : 0 : return (EPKG_FATAL );
502 : : }
503 : : }
504 [ - + ]: 8 : if (WEXITSTATUS(pstat) != 0) {
505 : 0 : pkg_emit_error("lua script failed");
506 : 0 : return (EPKG_FATAL);
507 : : }
508 : :
509 : 8 : return (EPKG_OK);
510 : 9 : }
511 : :
512 : : static void
513 : 350 : trigger_check_match(struct trigger *t, char *dir)
514 : : {
515 : : const ucl_object_t *cur;
516 : : ucl_object_iter_t it;
517 : :
518 : 350 : if (t->path != NULL) {
519 : 268 : it = NULL;
520 [ + + ]: 573 : while ((cur = ucl_iterate_object(t->path, &it, true))) {
521 [ + + ]: 306 : if (STREQ(dir, ucl_object_tostring(cur))) {
522 [ + - # # ]: 1 : pkghash_safe_add(t->matched, dir, dir, NULL);
523 : 1 : return;
524 : : }
525 : : }
526 : 267 : }
527 : :
528 : 528 : if (match_ucl_lists(dir, t->path_glob, t->path_regex)) {
529 [ + - # # ]: 6 : pkghash_safe_add(t->matched, dir, dir, NULL);
530 : 6 : }
531 : 350 : }
532 : :
533 : : /*
534 : : * first execute all triggers then the cleanup scripts
535 : : * from the triggers that are not there anymore
536 : : * Then execute all triggers
537 : : */
538 : : int
539 : 228 : triggers_execute(trigger_t *cleanup_triggers)
540 : : {
541 : : trigger_t *triggers;
542 : 228 : int ret = EPKG_OK;
543 : :
544 : 228 : triggers = triggers_load(false);
545 : 228 : pkg_emit_triggers_begin();
546 : 228 : if (cleanup_triggers != NULL) {
547 [ + - + + : 2 : tll_foreach(*cleanup_triggers, it) {
- + ]
548 : 1 : pkg_emit_trigger(it->item->name, true);
549 : 1 : if (it->item->cleanup.type == SCRIPT_LUA) {
550 : 2 : ret = trigger_execute_lua(it->item->cleanup.script,
551 : 1 : it->item->cleanup.sandbox, NULL);
552 : 1 : }
553 [ - + ]: 1 : if (ret != EPKG_OK)
554 : 0 : goto cleanup;
555 : 1 : }
556 : 1 : }
557 : :
558 : 97 : if (ctx.touched_dir_hash) {
559 : 97 : pkghash_it it = pkghash_iterator(ctx.touched_dir_hash);
560 [ + + ]: 201 : while (pkghash_next(&it)) {
561 [ + + + + : 454 : tll_foreach(*triggers, t)
+ + ]
562 : 350 : trigger_check_match(t->item, it.key);
563 : : /* We need to check if that matches a trigger */
564 : : }
565 : 97 : }
566 : :
567 [ + + + + : 1532 : tll_foreach(*triggers, it) {
+ + ]
568 [ + + ]: 1304 : if (it->item->matched == NULL)
569 : 1297 : continue;
570 : 7 : pkg_emit_trigger(it->item->name, false);
571 : 7 : if (it->item->script.type == SCRIPT_LUA) {
572 : 14 : ret = trigger_execute_lua(it->item->script.script,
573 : 7 : it->item->script.sandbox, it->item->matched);
574 : 7 : }
575 [ - + ]: 7 : if (ret != EPKG_OK)
576 : 0 : goto cleanup;
577 : 7 : }
578 : 228 : pkg_emit_triggers_finished();
579 : :
580 : : cleanup:
581 [ + + + + : 1532 : tll_free_and_free(*triggers, trigger_free);
+ + ]
582 : 228 : free(triggers);
583 : :
584 : 228 : return (EPKG_OK);
585 : : }
586 : :
587 : : void
588 : 6 : append_touched_dir(const char *path)
589 : : {
590 [ + + + + ]: 8 : pkghash_safe_add(ctx.touched_dir_hash, path, NULL, NULL);
591 : 6 : }
592 : :
593 : : void
594 : 174 : append_touched_file(const char *path)
595 : : {
596 : : char *newpath, *walk;
597 : :
598 : 174 : newpath = xstrdup(path);
599 : 174 : walk = strrchr(newpath, '/');
600 [ - + ]: 174 : if (walk == NULL)
601 : 0 : return;
602 : 174 : *walk = '\0';
603 : :
604 [ + + + + ]: 179 : pkghash_safe_add(ctx.touched_dir_hash, newpath, NULL, NULL );
605 : 174 : free(newpath);
606 : 174 : }
607 : :
608 : : void
609 : 1 : exec_deferred(int dfd, const char *name)
610 : : {
611 : 1 : bool sandbox = false;
612 : 1 : pkghash *args = NULL;
613 : 1 : xstring *script = NULL;
614 : :
615 : 1 : int fd = openat(dfd, name, O_RDONLY);
616 [ - + ]: 1 : if (fd == -1) {
617 : 0 : pkg_errno("Unable to open the trigger '%s'", name);
618 : 0 : return;
619 : : }
620 : 1 : FILE *f = fdopen(fd, "r");
621 [ - + ]: 1 : if (f == NULL) {
622 : 0 : pkg_errno("Unable to open the trigger '%s'", name);
623 : 0 : return;
624 : : }
625 : :
626 : 1 : char *line = NULL;
627 : 1 : size_t linecap = 0;
628 : : ssize_t linelen;
629 : : char *walk;
630 : 1 : bool inargs = false;
631 [ + + ]: 7 : while ((linelen = getline(&line, &linecap, f)) > 0) {
632 : 6 : walk = line;
633 : 6 : walk += 2; /* '--' aka lua comments */
634 [ + + ]: 6 : if (strncmp(walk, "sandbox", 7) == 0) {
635 : 1 : sandbox = true;
636 : 1 : continue;
637 : : }
638 [ + + ]: 5 : if (strncmp(walk, "begin args", 10) == 0) {
639 : 1 : inargs = true;
640 : 1 : continue;
641 : : }
642 [ + + ]: 4 : if (strncmp(walk, "end args", 8) == 0) {
643 : 1 : inargs = false;
644 : 1 : script = xstring_new();
645 : 1 : continue;
646 : : }
647 : 3 : if (inargs) {
648 : 1 : walk++; /* skip the space */
649 : 1 : if (line[linelen -1] == '\n')
650 : 1 : line[linelen -1] = '\0';
651 [ + - # # ]: 1 : pkghash_safe_add(args, walk, NULL, NULL);
652 : 1 : }
653 : 2 : if (script != NULL)
654 : 2 : fputs(line, script->fp);
655 : : }
656 : 1 : free(line);
657 : 1 : fclose(f);
658 [ - + ]: 1 : if (script == NULL) {
659 : 0 : pkghash_destroy(args);
660 : 0 : return;
661 : : }
662 : 1 : char *s = xstring_get(script);
663 : 1 : if (trigger_execute_lua(s, sandbox, args) == EPKG_OK) {
664 : 1 : unlinkat(dfd, name, 0);
665 : 1 : }
666 : 1 : free(s);
667 : 1 : pkghash_destroy(args);
668 : 1 : }
669 : :
670 : : int
671 : 1 : pkg_execute_deferred_triggers(void)
672 : : {
673 : : struct dirent *e;
674 : : struct stat st;
675 : 1 : int dbdir = pkg_get_dbdirfd();
676 : :
677 : 1 : int trigfd = openat(dbdir, "triggers", O_DIRECTORY);
678 [ - + ]: 1 : if (trigfd == -1)
679 : 0 : return (EPKG_OK);
680 : :
681 : 1 : DIR *d = fdopendir(trigfd);
682 [ - + ]: 1 : if (d == NULL) {
683 : 0 : close(trigfd);
684 : 0 : pkg_emit_error("Unable to open the deferred trigger directory");
685 : 0 : return (EPKG_FATAL);
686 : : }
687 : :
688 [ + + ]: 4 : while ((e = readdir(d)) != NULL) {
689 : : /* ignore all hiddn files */
690 [ + + ]: 3 : if (e->d_name[0] == '.')
691 : 2 : continue;
692 : : /* only regular files are considered */
693 [ - + ]: 1 : if (fstatat(trigfd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) {
694 : 0 : pkg_emit_errno("fstatat", e->d_name);
695 : 0 : return (EPKG_FATAL);
696 : : }
697 : 1 : exec_deferred(trigfd, e->d_name);
698 : : }
699 : 1 : closedir(d);
700 : 1 : return (EPKG_OK);
701 : 1 : }
|