1 /*
2 * hook.c
3 *
4 * Copyright (c) 2015-2018 Pacman Development Team <pacman-dev@archlinux.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <ctype.h>
21 #include <dirent.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <string.h>
25
26 #include "handle.h"
27 #include "hook.h"
28 #include "ini.h"
29 #include "log.h"
30 #include "trans.h"
31 #include "util.h"
32
33 enum _alpm_hook_op_t {
34 ALPM_HOOK_OP_INSTALL = (1 << 0),
35 ALPM_HOOK_OP_UPGRADE = (1 << 1),
36 ALPM_HOOK_OP_REMOVE = (1 << 2),
37 };
38
39 enum _alpm_trigger_type_t {
40 ALPM_HOOK_TYPE_PACKAGE = 1,
41 ALPM_HOOK_TYPE_FILE,
42 };
43
44 struct _alpm_trigger_t {
45 enum _alpm_hook_op_t op;
46 enum _alpm_trigger_type_t type;
47 alpm_list_t *targets;
48 };
49
50 struct _alpm_hook_t {
51 char *name;
52 char *desc;
53 alpm_list_t *triggers;
54 alpm_list_t *depends;
55 char **cmd;
56 alpm_list_t *matches;
57 alpm_hook_when_t when;
58 int abort_on_fail, needs_targets;
59 };
60
61 struct _alpm_hook_cb_ctx {
62 alpm_handle_t *handle;
63 struct _alpm_hook_t *hook;
64 };
65
_alpm_trigger_free(struct _alpm_trigger_t * trigger)66 static void _alpm_trigger_free(struct _alpm_trigger_t *trigger)
67 {
68 if(trigger) {
69 FREELIST(trigger->targets);
70 free(trigger);
71 }
72 }
73
_alpm_wordsplit_free(char ** ws)74 static void _alpm_wordsplit_free(char **ws)
75 {
76 if(ws) {
77 char **c;
78 for(c = ws; *c; c++) {
79 free(*c);
80 }
81 free(ws);
82 }
83 }
84
_alpm_hook_free(struct _alpm_hook_t * hook)85 static void _alpm_hook_free(struct _alpm_hook_t *hook)
86 {
87 if(hook) {
88 free(hook->name);
89 free(hook->desc);
90 _alpm_wordsplit_free(hook->cmd);
91 alpm_list_free_inner(hook->triggers, (alpm_list_fn_free) _alpm_trigger_free);
92 alpm_list_free(hook->triggers);
93 alpm_list_free(hook->matches);
94 FREELIST(hook->depends);
95 free(hook);
96 }
97 }
98
_alpm_trigger_validate(alpm_handle_t * handle,struct _alpm_trigger_t * trigger,const char * file)99 static int _alpm_trigger_validate(alpm_handle_t *handle,
100 struct _alpm_trigger_t *trigger, const char *file)
101 {
102 int ret = 0;
103
104 if(trigger->targets == NULL) {
105 ret = -1;
106 _alpm_log(handle, ALPM_LOG_ERROR,
107 _("Missing trigger targets in hook: %s\n"), file);
108 }
109
110 if(trigger->type == 0) {
111 ret = -1;
112 _alpm_log(handle, ALPM_LOG_ERROR,
113 _("Missing trigger type in hook: %s\n"), file);
114 }
115
116 if(trigger->op == 0) {
117 ret = -1;
118 _alpm_log(handle, ALPM_LOG_ERROR,
119 _("Missing trigger operation in hook: %s\n"), file);
120 }
121
122 return ret;
123 }
124
_alpm_hook_validate(alpm_handle_t * handle,struct _alpm_hook_t * hook,const char * file)125 static int _alpm_hook_validate(alpm_handle_t *handle,
126 struct _alpm_hook_t *hook, const char *file)
127 {
128 alpm_list_t *i;
129 int ret = 0;
130
131 if(hook->triggers == NULL) {
132 /* special case: allow triggerless hooks as a way of creating dummy
133 * hooks that can be used to mask lower priority hooks */
134 return 0;
135 }
136
137 for(i = hook->triggers; i; i = i->next) {
138 if(_alpm_trigger_validate(handle, i->data, file) != 0) {
139 ret = -1;
140 }
141 }
142
143 if(hook->cmd == NULL) {
144 ret = -1;
145 _alpm_log(handle, ALPM_LOG_ERROR,
146 _("Missing Exec option in hook: %s\n"), file);
147 }
148
149 if(hook->when == 0) {
150 ret = -1;
151 _alpm_log(handle, ALPM_LOG_ERROR,
152 _("Missing When option in hook: %s\n"), file);
153 } else if(hook->when != ALPM_HOOK_PRE_TRANSACTION && hook->abort_on_fail) {
154 _alpm_log(handle, ALPM_LOG_WARNING,
155 _("AbortOnFail set for PostTransaction hook: %s\n"), file);
156 }
157
158 return ret;
159 }
160
_alpm_wordsplit(char * str)161 static char **_alpm_wordsplit(char *str)
162 {
163 char *c = str, *end;
164 char **out = NULL, **outsave;
165 size_t count = 0;
166
167 if(str == NULL) {
168 errno = EINVAL;
169 return NULL;
170 }
171
172 for(c = str; isspace(*c); c++);
173 while(*c) {
174 size_t wordlen = 0;
175
176 /* extend our array */
177 outsave = out;
178 if((out = realloc(out, (count + 1) * sizeof(char*))) == NULL) {
179 out = outsave;
180 goto error;
181 }
182
183 /* calculate word length and check for unbalanced quotes */
184 for(end = c; *end && !isspace(*end); end++) {
185 if(*end == '\'' || *end == '"') {
186 char quote = *end;
187 while(*(++end) && *end != quote) {
188 if(*end == '\\' && *(end + 1) == quote) {
189 end++;
190 }
191 wordlen++;
192 }
193 if(*end != quote) {
194 errno = EINVAL;
195 goto error;
196 }
197 } else {
198 if(*end == '\\' && (end[1] == '\'' || end[1] == '"')) {
199 end++; /* skip the '\\' */
200 }
201 wordlen++;
202 }
203 }
204
205 if(wordlen == (size_t) (end - c)) {
206 /* no internal quotes or escapes, copy it the easy way */
207 if((out[count++] = strndup(c, wordlen)) == NULL) {
208 goto error;
209 }
210 } else {
211 /* manually copy to remove quotes and escapes */
212 char *dest = out[count++] = malloc(wordlen + 1);
213 if(dest == NULL) { goto error; }
214 while(c < end) {
215 if(*c == '\'' || *c == '"') {
216 char quote = *c;
217 /* we know there must be a matching end quote,
218 * no need to check for '\0' */
219 for(c++; *c != quote; c++) {
220 if(*c == '\\' && *(c + 1) == quote) {
221 c++;
222 }
223 *(dest++) = *c;
224 }
225 c++;
226 } else {
227 if(*c == '\\' && (c[1] == '\'' || c[1] == '"')) {
228 c++; /* skip the '\\' */
229 }
230 *(dest++) = *(c++);
231 }
232 }
233 *dest = '\0';
234 }
235
236 if(*end == '\0') {
237 break;
238 } else {
239 for(c = end + 1; isspace(*c); c++);
240 }
241 }
242
243 outsave = out;
244 if((out = realloc(out, (count + 1) * sizeof(char*))) == NULL) {
245 out = outsave;
246 goto error;
247 }
248
249 out[count++] = NULL;
250
251 return out;
252
253 error:
254 /* can't use wordsplit_free here because NULL has not been appended */
255 while(count) {
256 free(out[--count]);
257 }
258 free(out);
259 return NULL;
260 }
261
_alpm_hook_parse_cb(const char * file,int line,const char * section,char * key,char * value,void * data)262 static int _alpm_hook_parse_cb(const char *file, int line,
263 const char *section, char *key, char *value, void *data)
264 {
265 struct _alpm_hook_cb_ctx *ctx = data;
266 alpm_handle_t *handle = ctx->handle;
267 struct _alpm_hook_t *hook = ctx->hook;
268
269 #define error(...) _alpm_log(handle, ALPM_LOG_ERROR, __VA_ARGS__); return 1;
270 #define warning(...) _alpm_log(handle, ALPM_LOG_WARNING, __VA_ARGS__);
271
272 if(!section && !key) {
273 error(_("error while reading hook %s: %s\n"), file, strerror(errno));
274 } else if(!section) {
275 error(_("hook %s line %d: invalid option %s\n"), file, line, key);
276 } else if(!key) {
277 /* beginning a new section */
278 if(strcmp(section, "Trigger") == 0) {
279 struct _alpm_trigger_t *t;
280 CALLOC(t, sizeof(struct _alpm_trigger_t), 1, return 1);
281 hook->triggers = alpm_list_add(hook->triggers, t);
282 } else if(strcmp(section, "Action") == 0) {
283 /* no special processing required */
284 } else {
285 error(_("hook %s line %d: invalid section %s\n"), file, line, section);
286 }
287 } else if(strcmp(section, "Trigger") == 0) {
288 struct _alpm_trigger_t *t = hook->triggers->prev->data;
289 if(strcmp(key, "Operation") == 0) {
290 if(strcmp(value, "Install") == 0) {
291 t->op |= ALPM_HOOK_OP_INSTALL;
292 } else if(strcmp(value, "Upgrade") == 0) {
293 t->op |= ALPM_HOOK_OP_UPGRADE;
294 } else if(strcmp(value, "Remove") == 0) {
295 t->op |= ALPM_HOOK_OP_REMOVE;
296 } else {
297 error(_("hook %s line %d: invalid value %s\n"), file, line, value);
298 }
299 } else if(strcmp(key, "Type") == 0) {
300 if(t->type != 0) {
301 warning(_("hook %s line %d: overwriting previous definition of %s\n"), file, line, "Type");
302 }
303 if(strcmp(value, "Package") == 0) {
304 t->type = ALPM_HOOK_TYPE_PACKAGE;
305 } else if(strcmp(value, "File") == 0) {
306 t->type = ALPM_HOOK_TYPE_FILE;
307 } else {
308 error(_("hook %s line %d: invalid value %s\n"), file, line, value);
309 }
310 } else if(strcmp(key, "Target") == 0) {
311 char *val;
312 STRDUP(val, value, return 1);
313 t->targets = alpm_list_add(t->targets, val);
314 } else {
315 error(_("hook %s line %d: invalid option %s\n"), file, line, key);
316 }
317 } else if(strcmp(section, "Action") == 0) {
318 if(strcmp(key, "When") == 0) {
319 if(hook->when != 0) {
320 warning(_("hook %s line %d: overwriting previous definition of %s\n"), file, line, "When");
321 }
322 if(strcmp(value, "PreTransaction") == 0) {
323 hook->when = ALPM_HOOK_PRE_TRANSACTION;
324 } else if(strcmp(value, "PostTransaction") == 0) {
325 hook->when = ALPM_HOOK_POST_TRANSACTION;
326 } else {
327 error(_("hook %s line %d: invalid value %s\n"), file, line, value);
328 }
329 } else if(strcmp(key, "Description") == 0) {
330 if(hook->desc != NULL) {
331 warning(_("hook %s line %d: overwriting previous definition of %s\n"), file, line, "Description");
332 FREE(hook->desc);
333 }
334 STRDUP(hook->desc, value, return 1);
335 } else if(strcmp(key, "Depends") == 0) {
336 char *val;
337 STRDUP(val, value, return 1);
338 hook->depends = alpm_list_add(hook->depends, val);
339 } else if(strcmp(key, "AbortOnFail") == 0) {
340 hook->abort_on_fail = 1;
341 } else if(strcmp(key, "NeedsTargets") == 0) {
342 hook->needs_targets = 1;
343 } else if(strcmp(key, "Exec") == 0) {
344 if(hook->cmd != NULL) {
345 warning(_("hook %s line %d: overwriting previous definition of %s\n"), file, line, "Exec");
346 _alpm_wordsplit_free(hook->cmd);
347 }
348 if((hook->cmd = _alpm_wordsplit(value)) == NULL) {
349 if(errno == EINVAL) {
350 error(_("hook %s line %d: invalid value %s\n"), file, line, value);
351 } else {
352 error(_("hook %s line %d: unable to set option (%s)\n"),
353 file, line, strerror(errno));
354 }
355 }
356 } else {
357 error(_("hook %s line %d: invalid option %s\n"), file, line, key);
358 }
359 }
360
361 #undef error
362 #undef warning
363
364 return 0;
365 }
366
_alpm_hook_trigger_match_file(alpm_handle_t * handle,struct _alpm_hook_t * hook,struct _alpm_trigger_t * t)367 static int _alpm_hook_trigger_match_file(alpm_handle_t *handle,
368 struct _alpm_hook_t *hook, struct _alpm_trigger_t *t)
369 {
370 alpm_list_t *i, *j, *install = NULL, *upgrade = NULL, *remove = NULL;
371 size_t isize = 0, rsize = 0;
372 int ret = 0;
373
374 /* check if file will be installed */
375 for(i = handle->trans->add; i; i = i->next) {
376 alpm_pkg_t *pkg = i->data;
377 alpm_filelist_t filelist = pkg->files;
378 size_t f;
379 for(f = 0; f < filelist.count; f++) {
380 if(alpm_option_match_noextract(handle, filelist.files[f].name) == 0) {
381 continue;
382 }
383 if(_alpm_fnmatch_patterns(t->targets, filelist.files[f].name) == 0) {
384 install = alpm_list_add(install, filelist.files[f].name);
385 isize++;
386 }
387 }
388 }
389
390 /* check if file will be removed due to package upgrade */
391 for(i = handle->trans->add; i; i = i->next) {
392 alpm_pkg_t *spkg = i->data;
393 alpm_pkg_t *pkg = spkg->oldpkg;
394 if(pkg) {
395 alpm_filelist_t filelist = pkg->files;
396 size_t f;
397 for(f = 0; f < filelist.count; f++) {
398 if(_alpm_fnmatch_patterns(t->targets, filelist.files[f].name) == 0) {
399 remove = alpm_list_add(remove, filelist.files[f].name);
400 rsize++;
401 }
402 }
403 }
404 }
405
406 /* check if file will be removed due to package removal */
407 for(i = handle->trans->remove; i; i = i->next) {
408 alpm_pkg_t *pkg = i->data;
409 alpm_filelist_t filelist = pkg->files;
410 size_t f;
411 for(f = 0; f < filelist.count; f++) {
412 if(_alpm_fnmatch_patterns(t->targets, filelist.files[f].name) == 0) {
413 remove = alpm_list_add(remove, filelist.files[f].name);
414 rsize++;
415 }
416 }
417 }
418
419 i = install = alpm_list_msort(install, isize, (alpm_list_fn_cmp)strcmp);
420 j = remove = alpm_list_msort(remove, rsize, (alpm_list_fn_cmp)strcmp);
421 while(i) {
422 while(j && strcmp(i->data, j->data) > 0) {
423 j = j->next;
424 }
425 if(j == NULL) {
426 break;
427 }
428 if(strcmp(i->data, j->data) == 0) {
429 char *path = i->data;
430 upgrade = alpm_list_add(upgrade, path);
431 while(i && strcmp(i->data, path) == 0) {
432 alpm_list_t *next = i->next;
433 install = alpm_list_remove_item(install, i);
434 free(i);
435 i = next;
436 }
437 while(j && strcmp(j->data, path) == 0) {
438 alpm_list_t *next = j->next;
439 remove = alpm_list_remove_item(remove, j);
440 free(j);
441 j = next;
442 }
443 } else {
444 i = i->next;
445 }
446 }
447
448 ret = (t->op & ALPM_HOOK_OP_INSTALL && install)
449 || (t->op & ALPM_HOOK_OP_UPGRADE && upgrade)
450 || (t->op & ALPM_HOOK_OP_REMOVE && remove);
451
452 if(hook->needs_targets) {
453 #define _save_matches(_op, _matches) \
454 if(t->op & _op && _matches) { \
455 hook->matches = alpm_list_join(hook->matches, _matches); \
456 } else { \
457 alpm_list_free(_matches); \
458 }
459 _save_matches(ALPM_HOOK_OP_INSTALL, install);
460 _save_matches(ALPM_HOOK_OP_UPGRADE, upgrade);
461 _save_matches(ALPM_HOOK_OP_REMOVE, remove);
462 #undef _save_matches
463 } else {
464 alpm_list_free(install);
465 alpm_list_free(upgrade);
466 alpm_list_free(remove);
467 }
468
469 return ret;
470 }
471
_alpm_hook_trigger_match_pkg(alpm_handle_t * handle,struct _alpm_hook_t * hook,struct _alpm_trigger_t * t)472 static int _alpm_hook_trigger_match_pkg(alpm_handle_t *handle,
473 struct _alpm_hook_t *hook, struct _alpm_trigger_t *t)
474 {
475 alpm_list_t *install = NULL, *upgrade = NULL, *remove = NULL;
476
477 if(t->op & ALPM_HOOK_OP_INSTALL || t->op & ALPM_HOOK_OP_UPGRADE) {
478 alpm_list_t *i;
479 for(i = handle->trans->add; i; i = i->next) {
480 alpm_pkg_t *pkg = i->data;
481 if(_alpm_fnmatch_patterns(t->targets, pkg->name) == 0) {
482 if(pkg->oldpkg) {
483 if(t->op & ALPM_HOOK_OP_UPGRADE) {
484 if(hook->needs_targets) {
485 upgrade = alpm_list_add(upgrade, pkg->name);
486 } else {
487 return 1;
488 }
489 }
490 } else {
491 if(t->op & ALPM_HOOK_OP_INSTALL) {
492 if(hook->needs_targets) {
493 install = alpm_list_add(install, pkg->name);
494 } else {
495 return 1;
496 }
497 }
498 }
499 }
500 }
501 }
502
503 if(t->op & ALPM_HOOK_OP_REMOVE) {
504 alpm_list_t *i;
505 for(i = handle->trans->remove; i; i = i->next) {
506 alpm_pkg_t *pkg = i->data;
507 if(pkg && _alpm_fnmatch_patterns(t->targets, pkg->name) == 0) {
508 if(!alpm_list_find(handle->trans->add, pkg, _alpm_pkg_cmp)) {
509 if(hook->needs_targets) {
510 remove = alpm_list_add(remove, pkg->name);
511 } else {
512 return 1;
513 }
514 }
515 }
516 }
517 }
518
519 /* if we reached this point we either need the target lists or we didn't
520 * match anything and the following calls will all be no-ops */
521 hook->matches = alpm_list_join(hook->matches, install);
522 hook->matches = alpm_list_join(hook->matches, upgrade);
523 hook->matches = alpm_list_join(hook->matches, remove);
524
525 return install || upgrade || remove;
526 }
527
_alpm_hook_trigger_match(alpm_handle_t * handle,struct _alpm_hook_t * hook,struct _alpm_trigger_t * t)528 static int _alpm_hook_trigger_match(alpm_handle_t *handle,
529 struct _alpm_hook_t *hook, struct _alpm_trigger_t *t)
530 {
531 return t->type == ALPM_HOOK_TYPE_PACKAGE
532 ? _alpm_hook_trigger_match_pkg(handle, hook, t)
533 : _alpm_hook_trigger_match_file(handle, hook, t);
534 }
535
_alpm_hook_triggered(alpm_handle_t * handle,struct _alpm_hook_t * hook)536 static int _alpm_hook_triggered(alpm_handle_t *handle, struct _alpm_hook_t *hook)
537 {
538 alpm_list_t *i;
539 int ret = 0;
540 for(i = hook->triggers; i; i = i->next) {
541 if(_alpm_hook_trigger_match(handle, hook, i->data)) {
542 if(!hook->needs_targets) {
543 return 1;
544 } else {
545 ret = 1;
546 }
547 }
548 }
549 return ret;
550 }
551
_alpm_hook_cmp(struct _alpm_hook_t * h1,struct _alpm_hook_t * h2)552 static int _alpm_hook_cmp(struct _alpm_hook_t *h1, struct _alpm_hook_t *h2)
553 {
554 return strcmp(h1->name, h2->name);
555 }
556
find_hook(alpm_list_t * haystack,const void * needle)557 static alpm_list_t *find_hook(alpm_list_t *haystack, const void *needle)
558 {
559 while(haystack) {
560 struct _alpm_hook_t *h = haystack->data;
561 if(h && strcmp(h->name, needle) == 0) {
562 return haystack;
563 }
564 haystack = haystack->next;
565 }
566 return NULL;
567 }
568
_alpm_hook_feed_targets(char * buf,ssize_t needed,alpm_list_t ** pos)569 static ssize_t _alpm_hook_feed_targets(char *buf, ssize_t needed, alpm_list_t **pos)
570 {
571 size_t remaining = needed, written = 0;;
572 size_t len;
573
574 while(*pos && (len = strlen((*pos)->data)) + 1 <= remaining) {
575 memcpy(buf, (*pos)->data, len);
576 buf[len++] = '\n';
577 *pos = (*pos)->next;
578 buf += len;
579 remaining -= len;
580 written += len;
581 }
582
583 if(*pos && remaining) {
584 memcpy(buf, (*pos)->data, remaining);
585 (*pos)->data = (char*) (*pos)->data + remaining;
586 written += remaining;
587 }
588
589 return written;
590 }
591
_alpm_strlist_dedup(alpm_list_t * list)592 static alpm_list_t *_alpm_strlist_dedup(alpm_list_t *list)
593 {
594 alpm_list_t *i = list;
595 while(i) {
596 alpm_list_t *next = i->next;
597 while(next && strcmp(i->data, next->data) == 0) {
598 list = alpm_list_remove_item(list, next);
599 free(next);
600 next = i->next;
601 }
602 i = next;
603 }
604 return list;
605 }
606
_alpm_hook_run_hook(alpm_handle_t * handle,struct _alpm_hook_t * hook)607 static int _alpm_hook_run_hook(alpm_handle_t *handle, struct _alpm_hook_t *hook)
608 {
609 alpm_list_t *i, *pkgs = _alpm_db_get_pkgcache(handle->db_local);
610
611 for(i = hook->depends; i; i = i->next) {
612 if(!alpm_find_satisfier(pkgs, i->data)) {
613 _alpm_log(handle, ALPM_LOG_ERROR, _("unable to run hook %s: %s\n"),
614 hook->name, _("could not satisfy dependencies"));
615 return -1;
616 }
617 }
618
619 if(hook->needs_targets) {
620 alpm_list_t *ctx;
621 hook->matches = alpm_list_msort(hook->matches,
622 alpm_list_count(hook->matches), (alpm_list_fn_cmp)strcmp);
623 /* hooks with multiple triggers could have duplicate matches */
624 ctx = hook->matches = _alpm_strlist_dedup(hook->matches);
625 return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd,
626 (_alpm_cb_io) _alpm_hook_feed_targets, &ctx);
627 } else {
628 return _alpm_run_chroot(handle, hook->cmd[0], hook->cmd, NULL, NULL);
629 }
630 }
631
_alpm_hook_run(alpm_handle_t * handle,alpm_hook_when_t when)632 int _alpm_hook_run(alpm_handle_t *handle, alpm_hook_when_t when)
633 {
634 alpm_event_hook_t event = { .when = when };
635 alpm_event_hook_run_t hook_event;
636 alpm_list_t *i, *hooks = NULL, *hooks_triggered = NULL;
637 const char *suffix = ".hook";
638 size_t suflen = strlen(suffix), triggered = 0;
639 int ret = 0;
640
641 for(i = alpm_list_last(handle->hookdirs); i; i = alpm_list_previous(i)) {
642 char path[PATH_MAX];
643 size_t dirlen;
644 struct dirent *entry;
645 DIR *d;
646
647 if((dirlen = strlen(i->data)) >= PATH_MAX) {
648 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open directory: %s: %s\n"),
649 (char *)i->data, strerror(ENAMETOOLONG));
650 ret = -1;
651 continue;
652 }
653 memcpy(path, i->data, dirlen + 1);
654
655 if(!(d = opendir(path))) {
656 if(errno == ENOENT) {
657 continue;
658 } else {
659 _alpm_log(handle, ALPM_LOG_ERROR,
660 _("could not open directory: %s: %s\n"), path, strerror(errno));
661 ret = -1;
662 continue;
663 }
664 }
665
666 while((errno = 0, entry = readdir(d))) {
667 struct _alpm_hook_cb_ctx ctx = { handle, NULL };
668 struct stat buf;
669 size_t name_len;
670
671 if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
672 continue;
673 }
674
675 if((name_len = strlen(entry->d_name)) >= PATH_MAX - dirlen) {
676 _alpm_log(handle, ALPM_LOG_ERROR, _("could not open file: %s%s: %s\n"),
677 path, entry->d_name, strerror(ENAMETOOLONG));
678 ret = -1;
679 continue;
680 }
681 memcpy(path + dirlen, entry->d_name, name_len + 1);
682
683 if(name_len < suflen
684 || strcmp(entry->d_name + name_len - suflen, suffix) != 0) {
685 _alpm_log(handle, ALPM_LOG_DEBUG, "skipping non-hook file %s\n", path);
686 continue;
687 }
688
689 if(find_hook(hooks, entry->d_name)) {
690 _alpm_log(handle, ALPM_LOG_DEBUG, "skipping overridden hook %s\n", path);
691 continue;
692 }
693
694 if(stat(path, &buf) != 0) {
695 _alpm_log(handle, ALPM_LOG_ERROR,
696 _("could not stat file %s: %s\n"), path, strerror(errno));
697 ret = -1;
698 continue;
699 }
700
701 if(S_ISDIR(buf.st_mode)) {
702 _alpm_log(handle, ALPM_LOG_DEBUG, "skipping directory %s\n", path);
703 continue;
704 }
705
706 CALLOC(ctx.hook, sizeof(struct _alpm_hook_t), 1,
707 ret = -1; closedir(d); goto cleanup);
708
709 _alpm_log(handle, ALPM_LOG_DEBUG, "parsing hook file %s\n", path);
710 if(parse_ini(path, _alpm_hook_parse_cb, &ctx) != 0
711 || _alpm_hook_validate(handle, ctx.hook, path)) {
712 _alpm_log(handle, ALPM_LOG_DEBUG, "parsing hook file %s failed\n", path);
713 _alpm_hook_free(ctx.hook);
714 ret = -1;
715 continue;
716 }
717
718 STRDUP(ctx.hook->name, entry->d_name, ret = -1; closedir(d); goto cleanup);
719 hooks = alpm_list_add(hooks, ctx.hook);
720 }
721 if(errno != 0) {
722 _alpm_log(handle, ALPM_LOG_ERROR, _("could not read directory: %s: %s\n"),
723 (char *) i->data, strerror(errno));
724 ret = -1;
725 }
726
727 closedir(d);
728 }
729
730 if(ret != 0 && when == ALPM_HOOK_PRE_TRANSACTION) {
731 goto cleanup;
732 }
733
734 hooks = alpm_list_msort(hooks, alpm_list_count(hooks),
735 (alpm_list_fn_cmp)_alpm_hook_cmp);
736
737 for(i = hooks; i; i = i->next) {
738 struct _alpm_hook_t *hook = i->data;
739 if(hook && hook->when == when && _alpm_hook_triggered(handle, hook)) {
740 hooks_triggered = alpm_list_add(hooks_triggered, hook);
741 triggered++;
742 }
743 }
744
745 if(hooks_triggered != NULL) {
746 event.type = ALPM_EVENT_HOOK_START;
747 EVENT(handle, (void *)&event);
748
749 hook_event.position = 1;
750 hook_event.total = triggered;
751
752 for(i = hooks_triggered; i; i = i->next, hook_event.position++) {
753 struct _alpm_hook_t *hook = i->data;
754 alpm_logaction(handle, ALPM_CALLER_PREFIX, "running '%s'...\n", hook->name);
755
756 hook_event.type = ALPM_EVENT_HOOK_RUN_START;
757 hook_event.name = hook->name;
758 hook_event.desc = hook->desc;
759 EVENT(handle, &hook_event);
760
761 if(_alpm_hook_run_hook(handle, hook) != 0 && hook->abort_on_fail) {
762 ret = -1;
763 }
764
765 hook_event.type = ALPM_EVENT_HOOK_RUN_DONE;
766 EVENT(handle, &hook_event);
767
768 if(ret != 0 && when == ALPM_HOOK_PRE_TRANSACTION) {
769 break;
770 }
771 }
772
773 alpm_list_free(hooks_triggered);
774
775 event.type = ALPM_EVENT_HOOK_DONE;
776 EVENT(handle, (void *)&event);
777 }
778
779 cleanup:
780 alpm_list_free_inner(hooks, (alpm_list_fn_free) _alpm_hook_free);
781 alpm_list_free(hooks);
782
783 return ret;
784 }
785