LCOV - code coverage report
Current view: top level - libpkg - triggers.c (source / functions) Hit Total Coverage
Test: rapport Lines: 286 392 73.0 %
Date: 2021-12-10 16:22:55 Functions: 13 13 100.0 %
Branches: 113 178 63.5 %

           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 : }

Generated by: LCOV version 1.15