1 |
|
|
/* $OpenBSD: hooks.c,v 1.4 2015/12/16 21:50:37 nicm Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org> |
5 |
|
|
* |
6 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
7 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
8 |
|
|
* copyright notice and this permission notice appear in all copies. |
9 |
|
|
* |
10 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER |
15 |
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING |
16 |
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 |
|
|
*/ |
18 |
|
|
|
19 |
|
|
#include <sys/types.h> |
20 |
|
|
|
21 |
|
|
#include <stdlib.h> |
22 |
|
|
#include <string.h> |
23 |
|
|
|
24 |
|
|
#include "tmux.h" |
25 |
|
|
|
26 |
|
|
struct hooks { |
27 |
|
|
RB_HEAD(hooks_tree, hook) tree; |
28 |
|
|
struct hooks *parent; |
29 |
|
|
}; |
30 |
|
|
|
31 |
|
|
static int hooks_cmp(struct hook *, struct hook *); |
32 |
|
|
RB_PROTOTYPE(hooks_tree, hook, entry, hooks_cmp); |
33 |
|
|
RB_GENERATE(hooks_tree, hook, entry, hooks_cmp); |
34 |
|
|
|
35 |
|
|
static struct hook *hooks_find1(struct hooks *, const char *); |
36 |
|
|
static void hooks_free1(struct hooks *, struct hook *); |
37 |
|
|
static void hooks_emptyfn(struct cmd_q *); |
38 |
|
|
|
39 |
|
|
static int |
40 |
|
|
hooks_cmp(struct hook *hook1, struct hook *hook2) |
41 |
|
|
{ |
42 |
|
|
return (strcmp(hook1->name, hook2->name)); |
43 |
|
|
} |
44 |
|
|
|
45 |
|
|
struct hooks * |
46 |
|
|
hooks_get(struct session *s) |
47 |
|
|
{ |
48 |
|
|
if (s != NULL) |
49 |
|
|
return (s->hooks); |
50 |
|
|
return (global_hooks); |
51 |
|
|
} |
52 |
|
|
|
53 |
|
|
struct hooks * |
54 |
|
|
hooks_create(struct hooks *parent) |
55 |
|
|
{ |
56 |
|
|
struct hooks *hooks; |
57 |
|
|
|
58 |
|
|
hooks = xcalloc(1, sizeof *hooks); |
59 |
|
|
RB_INIT(&hooks->tree); |
60 |
|
|
hooks->parent = parent; |
61 |
|
|
return (hooks); |
62 |
|
|
} |
63 |
|
|
|
64 |
|
|
static void |
65 |
|
|
hooks_free1(struct hooks *hooks, struct hook *hook) |
66 |
|
|
{ |
67 |
|
|
RB_REMOVE(hooks_tree, &hooks->tree, hook); |
68 |
|
|
cmd_list_free(hook->cmdlist); |
69 |
|
|
free((char *)hook->name); |
70 |
|
|
free(hook); |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
void |
74 |
|
|
hooks_free(struct hooks *hooks) |
75 |
|
|
{ |
76 |
|
|
struct hook *hook, *hook1; |
77 |
|
|
|
78 |
|
|
RB_FOREACH_SAFE(hook, hooks_tree, &hooks->tree, hook1) |
79 |
|
|
hooks_free1(hooks, hook); |
80 |
|
|
free(hooks); |
81 |
|
|
} |
82 |
|
|
|
83 |
|
|
struct hook * |
84 |
|
|
hooks_first(struct hooks *hooks) |
85 |
|
|
{ |
86 |
|
|
return (RB_MIN(hooks_tree, &hooks->tree)); |
87 |
|
|
} |
88 |
|
|
|
89 |
|
|
struct hook * |
90 |
|
|
hooks_next(struct hook *hook) |
91 |
|
|
{ |
92 |
|
|
return (RB_NEXT(hooks_tree, &hooks->tree, hook)); |
93 |
|
|
} |
94 |
|
|
|
95 |
|
|
void |
96 |
|
|
hooks_add(struct hooks *hooks, const char *name, struct cmd_list *cmdlist) |
97 |
|
|
{ |
98 |
|
|
struct hook *hook; |
99 |
|
|
|
100 |
|
|
if ((hook = hooks_find1(hooks, name)) != NULL) |
101 |
|
|
hooks_free1(hooks, hook); |
102 |
|
|
|
103 |
|
|
hook = xcalloc(1, sizeof *hook); |
104 |
|
|
hook->name = xstrdup(name); |
105 |
|
|
hook->cmdlist = cmdlist; |
106 |
|
|
hook->cmdlist->references++; |
107 |
|
|
RB_INSERT(hooks_tree, &hooks->tree, hook); |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
void |
111 |
|
|
hooks_remove(struct hooks *hooks, const char *name) |
112 |
|
|
{ |
113 |
|
|
struct hook *hook; |
114 |
|
|
|
115 |
|
|
if ((hook = hooks_find1(hooks, name)) != NULL) |
116 |
|
|
hooks_free1(hooks, hook); |
117 |
|
|
} |
118 |
|
|
|
119 |
|
|
static struct hook * |
120 |
|
|
hooks_find1(struct hooks *hooks, const char *name) |
121 |
|
|
{ |
122 |
|
|
struct hook hook; |
123 |
|
|
|
124 |
|
|
hook.name = name; |
125 |
|
|
return (RB_FIND(hooks_tree, &hooks->tree, &hook)); |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
struct hook * |
129 |
|
|
hooks_find(struct hooks *hooks, const char *name) |
130 |
|
|
{ |
131 |
|
|
struct hook hook0, *hook; |
132 |
|
|
|
133 |
|
|
hook0.name = name; |
134 |
|
|
hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); |
135 |
|
|
while (hook == NULL) { |
136 |
|
|
hooks = hooks->parent; |
137 |
|
|
if (hooks == NULL) |
138 |
|
|
break; |
139 |
|
|
hook = RB_FIND(hooks_tree, &hooks->tree, &hook0); |
140 |
|
|
} |
141 |
|
|
return (hook); |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
static void |
145 |
|
|
hooks_emptyfn(struct cmd_q *hooks_cmdq) |
146 |
|
|
{ |
147 |
|
|
struct cmd_q *cmdq = hooks_cmdq->data; |
148 |
|
|
|
149 |
|
|
if (cmdq != NULL) { |
150 |
|
|
if (hooks_cmdq->client_exit >= 0) |
151 |
|
|
cmdq->client_exit = hooks_cmdq->client_exit; |
152 |
|
|
if (!cmdq_free(cmdq)) |
153 |
|
|
cmdq_continue(cmdq); |
154 |
|
|
} |
155 |
|
|
cmdq_free(hooks_cmdq); |
156 |
|
|
} |
157 |
|
|
|
158 |
|
|
int |
159 |
|
|
hooks_run(struct hooks *hooks, struct client *c, struct cmd_find_state *fs, |
160 |
|
|
const char *fmt, ...) |
161 |
|
|
{ |
162 |
|
|
struct hook *hook; |
163 |
|
|
struct cmd_q *hooks_cmdq; |
164 |
|
|
va_list ap; |
165 |
|
|
char *name; |
166 |
|
|
|
167 |
|
|
va_start(ap, fmt); |
168 |
|
|
xvasprintf(&name, fmt, ap); |
169 |
|
|
va_end(ap); |
170 |
|
|
|
171 |
|
|
hook = hooks_find(hooks, name); |
172 |
|
|
if (hook == NULL) { |
173 |
|
|
free(name); |
174 |
|
|
return (-1); |
175 |
|
|
} |
176 |
|
|
log_debug("running hook %s", name); |
177 |
|
|
free(name); |
178 |
|
|
|
179 |
|
|
hooks_cmdq = cmdq_new(c); |
180 |
|
|
hooks_cmdq->flags |= CMD_Q_NOHOOKS; |
181 |
|
|
|
182 |
|
|
if (fs != NULL) |
183 |
|
|
cmd_find_copy_state(&hooks_cmdq->current, fs); |
184 |
|
|
hooks_cmdq->parent = NULL; |
185 |
|
|
|
186 |
|
|
cmdq_run(hooks_cmdq, hook->cmdlist, NULL); |
187 |
|
|
cmdq_free(hooks_cmdq); |
188 |
|
|
return (0); |
189 |
|
|
} |
190 |
|
|
|
191 |
|
|
int |
192 |
|
|
hooks_wait(struct hooks *hooks, struct cmd_q *cmdq, struct cmd_find_state *fs, |
193 |
|
|
const char *fmt, ...) |
194 |
|
|
{ |
195 |
|
|
struct hook *hook; |
196 |
|
|
struct cmd_q *hooks_cmdq; |
197 |
|
|
va_list ap; |
198 |
|
|
char *name; |
199 |
|
|
|
200 |
|
|
va_start(ap, fmt); |
201 |
|
|
xvasprintf(&name, fmt, ap); |
202 |
|
|
va_end(ap); |
203 |
|
|
|
204 |
|
|
hook = hooks_find(hooks, name); |
205 |
|
|
if (hook == NULL) { |
206 |
|
|
free(name); |
207 |
|
|
return (-1); |
208 |
|
|
} |
209 |
|
|
log_debug("running hook %s (parent %p)", name, cmdq); |
210 |
|
|
free(name); |
211 |
|
|
|
212 |
|
|
hooks_cmdq = cmdq_new(cmdq->client); |
213 |
|
|
hooks_cmdq->flags |= CMD_Q_NOHOOKS; |
214 |
|
|
|
215 |
|
|
if (fs != NULL) |
216 |
|
|
cmd_find_copy_state(&hooks_cmdq->current, fs); |
217 |
|
|
hooks_cmdq->parent = cmdq; |
218 |
|
|
|
219 |
|
|
hooks_cmdq->emptyfn = hooks_emptyfn; |
220 |
|
|
hooks_cmdq->data = cmdq; |
221 |
|
|
|
222 |
|
|
if (cmdq != NULL) |
223 |
|
|
cmdq->references++; |
224 |
|
|
cmdq_run(hooks_cmdq, hook->cmdlist, NULL); |
225 |
|
|
return (0); |
226 |
|
|
} |