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