The sixth batch
[alt-git.git] / hook.c
blobeebc4d44734a8040a238bd4bb89d101841288a5d
1 #include "git-compat-util.h"
2 #include "abspath.h"
3 #include "advice.h"
4 #include "gettext.h"
5 #include "hook.h"
6 #include "path.h"
7 #include "run-command.h"
8 #include "config.h"
9 #include "strbuf.h"
10 #include "environment.h"
11 #include "setup.h"
12 #include "copy.h"
14 static int identical_to_template_hook(const char *name, const char *path)
16 const char *env = getenv("GIT_CLONE_TEMPLATE_DIR");
17 const char *template_dir = get_template_dir(env && *env ? env : NULL);
18 struct strbuf template_path = STRBUF_INIT;
19 int found_template_hook, ret;
21 strbuf_addf(&template_path, "%s/hooks/%s", template_dir, name);
22 found_template_hook = access(template_path.buf, X_OK) >= 0;
23 #ifdef STRIP_EXTENSION
24 if (!found_template_hook) {
25 strbuf_addstr(&template_path, STRIP_EXTENSION);
26 found_template_hook = access(template_path.buf, X_OK) >= 0;
28 #endif
29 if (!found_template_hook)
30 return 0;
32 ret = do_files_match(template_path.buf, path);
34 strbuf_release(&template_path);
35 return ret;
38 const char *find_hook(const char *name)
40 static struct strbuf path = STRBUF_INIT;
42 int found_hook;
44 strbuf_reset(&path);
45 strbuf_git_path(&path, "hooks/%s", name);
46 found_hook = access(path.buf, X_OK) >= 0;
47 #ifdef STRIP_EXTENSION
48 if (!found_hook) {
49 int err = errno;
51 strbuf_addstr(&path, STRIP_EXTENSION);
52 found_hook = access(path.buf, X_OK) >= 0;
53 if (!found_hook)
54 errno = err;
56 #endif
58 if (!found_hook) {
59 if (errno == EACCES && advice_enabled(ADVICE_IGNORED_HOOK)) {
60 static struct string_list advise_given = STRING_LIST_INIT_DUP;
62 if (!string_list_lookup(&advise_given, name)) {
63 string_list_insert(&advise_given, name);
64 advise(_("The '%s' hook was ignored because "
65 "it's not set as executable.\n"
66 "You can disable this warning with "
67 "`git config advice.ignoredHook false`."),
68 path.buf);
71 return NULL;
73 if (!git_hooks_path && git_env_bool("GIT_CLONE_PROTECTION_ACTIVE", 0) &&
74 !identical_to_template_hook(name, path.buf))
75 die(_("active `%s` hook found during `git clone`:\n\t%s\n"
76 "For security reasons, this is disallowed by default.\n"
77 "If this is intentional and the hook should actually "
78 "be run, please\nrun the command again with "
79 "`GIT_CLONE_PROTECTION_ACTIVE=false`"),
80 name, path.buf);
81 return path.buf;
84 int hook_exists(const char *name)
86 return !!find_hook(name);
89 static int pick_next_hook(struct child_process *cp,
90 struct strbuf *out UNUSED,
91 void *pp_cb,
92 void **pp_task_cb UNUSED)
94 struct hook_cb_data *hook_cb = pp_cb;
95 const char *hook_path = hook_cb->hook_path;
97 if (!hook_path)
98 return 0;
100 cp->no_stdin = 1;
101 strvec_pushv(&cp->env, hook_cb->options->env.v);
102 /* reopen the file for stdin; run_command closes it. */
103 if (hook_cb->options->path_to_stdin) {
104 cp->no_stdin = 0;
105 cp->in = xopen(hook_cb->options->path_to_stdin, O_RDONLY);
107 cp->stdout_to_stderr = 1;
108 cp->trace2_hook_name = hook_cb->hook_name;
109 cp->dir = hook_cb->options->dir;
111 strvec_push(&cp->args, hook_path);
112 strvec_pushv(&cp->args, hook_cb->options->args.v);
115 * This pick_next_hook() will be called again, we're only
116 * running one hook, so indicate that no more work will be
117 * done.
119 hook_cb->hook_path = NULL;
121 return 1;
124 static int notify_start_failure(struct strbuf *out UNUSED,
125 void *pp_cb,
126 void *pp_task_cp UNUSED)
128 struct hook_cb_data *hook_cb = pp_cb;
130 hook_cb->rc |= 1;
132 return 1;
135 static int notify_hook_finished(int result,
136 struct strbuf *out UNUSED,
137 void *pp_cb,
138 void *pp_task_cb UNUSED)
140 struct hook_cb_data *hook_cb = pp_cb;
141 struct run_hooks_opt *opt = hook_cb->options;
143 hook_cb->rc |= result;
145 if (opt->invoked_hook)
146 *opt->invoked_hook = 1;
148 return 0;
151 static void run_hooks_opt_clear(struct run_hooks_opt *options)
153 strvec_clear(&options->env);
154 strvec_clear(&options->args);
157 int run_hooks_opt(const char *hook_name, struct run_hooks_opt *options)
159 struct strbuf abs_path = STRBUF_INIT;
160 struct hook_cb_data cb_data = {
161 .rc = 0,
162 .hook_name = hook_name,
163 .options = options,
165 const char *const hook_path = find_hook(hook_name);
166 int ret = 0;
167 const struct run_process_parallel_opts opts = {
168 .tr2_category = "hook",
169 .tr2_label = hook_name,
171 .processes = 1,
172 .ungroup = 1,
174 .get_next_task = pick_next_hook,
175 .start_failure = notify_start_failure,
176 .task_finished = notify_hook_finished,
178 .data = &cb_data,
181 if (!options)
182 BUG("a struct run_hooks_opt must be provided to run_hooks");
184 if (options->invoked_hook)
185 *options->invoked_hook = 0;
187 if (!hook_path && !options->error_if_missing)
188 goto cleanup;
190 if (!hook_path) {
191 ret = error("cannot find a hook named %s", hook_name);
192 goto cleanup;
195 cb_data.hook_path = hook_path;
196 if (options->dir) {
197 strbuf_add_absolute_path(&abs_path, hook_path);
198 cb_data.hook_path = abs_path.buf;
201 run_processes_parallel(&opts);
202 ret = cb_data.rc;
203 cleanup:
204 strbuf_release(&abs_path);
205 run_hooks_opt_clear(options);
206 return ret;
209 int run_hooks(const char *hook_name)
211 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
213 return run_hooks_opt(hook_name, &opt);
216 int run_hooks_l(const char *hook_name, ...)
218 struct run_hooks_opt opt = RUN_HOOKS_OPT_INIT;
219 va_list ap;
220 const char *arg;
222 va_start(ap, hook_name);
223 while ((arg = va_arg(ap, const char *)))
224 strvec_push(&opt.args, arg);
225 va_end(ap);
227 return run_hooks_opt(hook_name, &opt);