LCOV - code coverage report
Current view: top level - libpkg - triggers.c (source / functions) Hit Total Coverage
Test: plop Lines: 275 388 70.9 %
Date: 2024-12-30 07:09:03 Functions: 15 15 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 111 168 66.1 %

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

Generated by: LCOV version 1.15