360810ea8b20ce3cf06053db10310af17bc950c9
[project/procd.git] / service.c
1 #include <libubox/avl-cmp.h>
2 #include "procd.h"
3 #include "service.h"
4
5 struct avl_tree services;
6 static struct blob_buf b;
7
8 static void
9 start_instance(struct service_instance *in)
10 {
11 in->restart = false;
12 }
13
14 static void
15 instance_timeout(struct uloop_timeout *t)
16 {
17 struct service_instance *in;
18
19 in = container_of(t, struct service_instance, timeout);
20 kill(in->proc.pid, SIGKILL);
21 uloop_process_delete(&in->proc);
22 in->proc.cb(&in->proc, -1);
23 }
24
25 static void
26 instance_exit(struct uloop_process *p, int ret)
27 {
28 struct service_instance *in;
29
30 in = container_of(p, struct service_instance, proc);
31 uloop_timeout_cancel(&in->timeout);
32 if (in->restart)
33 start_instance(in);
34 }
35
36 static void
37 stop_instance(struct service_instance *in, bool restart)
38 {
39 if (!in->proc.pending)
40 return;
41
42 kill(in->proc.pid, SIGTERM);
43 }
44
45 static bool
46 instance_config_changed(struct service_instance *in, struct service_instance *in_new)
47 {
48 int len = blob_pad_len(in->config);
49
50 if (len != blob_pad_len(in_new->config))
51 return true;
52
53 if (memcmp(in->config, in_new->config, blob_pad_len(in->config)) != 0)
54 return true;
55
56 return false;
57 }
58
59 static bool
60 update_instance(struct service_instance *in, struct service_instance *in_new)
61 {
62 bool changed = instance_config_changed(in, in_new);
63
64 in->config = in_new->config;
65 if (!changed)
66 return false;
67
68 stop_instance(in, true);
69 return true;
70 }
71
72 static void
73 free_instance(struct service_instance *in)
74 {
75 uloop_process_delete(&in->proc);
76 uloop_timeout_cancel(&in->timeout);
77 free(in);
78 }
79
80 static void
81 init_instance(struct service_instance *in, struct blob_attr *config)
82 {
83 in->config = config;
84 in->timeout.cb = instance_timeout;
85 in->proc.cb = instance_exit;
86 }
87
88 static void
89 service_instance_add(struct service *s, struct blob_attr *attr)
90 {
91 struct service_instance *in;
92 const char *name = blobmsg_name(attr);
93
94 if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE)
95 return;
96
97 in = calloc(1, sizeof(*in));
98 if (!in)
99 return;
100
101 init_instance(in, attr);
102 vlist_add(&s->instances, &in->node, (void *) name);
103 }
104
105 static void
106 service_instance_update(struct vlist_tree *tree, struct vlist_node *node_new,
107 struct vlist_node *node_old)
108 {
109 struct service_instance *in_o = NULL, *in_n = NULL;
110
111 if (node_old)
112 in_o = container_of(node_old, struct service_instance, node);
113
114 if (node_new)
115 in_n = container_of(node_new, struct service_instance, node);
116
117 if (in_o && in_n) {
118 update_instance(in_o, in_n);
119 free_instance(in_n);
120 } else if (in_o) {
121 stop_instance(in_o, false);
122 free_instance(in_o);
123 } else if (in_n) {
124 start_instance(in_n);
125 }
126 }
127
128 static struct service *
129 service_alloc(const char *name)
130 {
131 struct service *s;
132
133 s = calloc(1, sizeof(*s));
134 vlist_init(&s->instances, avl_strcmp, service_instance_update);
135 s->instances.keep_old = true;
136
137 return s;
138 }
139
140 enum {
141 SERVICE_ATTR_NAME,
142 SERVICE_ATTR_SCRIPT,
143 SERVICE_ATTR_INSTANCES,
144 __SERVICE_ATTR_MAX
145 };
146
147 static const struct blobmsg_policy service_attrs[__SERVICE_ATTR_MAX] = {
148 [SERVICE_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
149 [SERVICE_ATTR_SCRIPT] = { "script", BLOBMSG_TYPE_STRING },
150 [SERVICE_ATTR_INSTANCES] = { "instances", BLOBMSG_TYPE_TABLE },
151 };
152
153
154 static int
155 service_update(struct service *s, struct blob_attr *config, struct blob_attr **tb)
156 {
157 struct blob_attr *old_config = s->config;
158 struct blob_attr *cur;
159 int rem;
160
161 /* only the pointer changes, the content stays the same,
162 * no avl update necessary */
163 s->name = s->avl.key = blobmsg_data(tb[SERVICE_ATTR_NAME]);
164 s->config = config;
165
166 if (tb[SERVICE_ATTR_INSTANCES]) {
167 vlist_update(&s->instances);
168 blobmsg_for_each_attr(cur, tb[SERVICE_ATTR_INSTANCES], rem) {
169 service_instance_add(s, cur);
170 }
171 vlist_flush(&s->instances);
172 }
173
174 free(old_config);
175
176 return 0;
177 }
178
179 static void
180 service_delete(struct service *s)
181 {
182 vlist_flush_all(&s->instances);
183 avl_delete(&services, &s->avl);
184 free(s->config);
185 free(s);
186 }
187
188 static int
189 service_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
190 struct ubus_request_data *req, const char *method,
191 struct blob_attr *msg)
192 {
193 struct blob_attr *tb[__SERVICE_ATTR_MAX], *cur;
194 struct service *s = NULL;
195 const char *name;
196 int ret = UBUS_STATUS_INVALID_ARGUMENT;
197
198 msg = blob_memdup(msg);
199 if (!msg)
200 return UBUS_STATUS_UNKNOWN_ERROR;
201
202 blobmsg_parse(service_attrs, __SERVICE_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
203 cur = tb[SERVICE_ATTR_NAME];
204 if (!cur)
205 goto free;
206
207 name = blobmsg_data(cur);
208
209 s = avl_find_element(&services, name, s, avl);
210 if (s)
211 return service_update(s, msg, tb);
212
213 s = service_alloc(name);
214 if (!s)
215 return UBUS_STATUS_UNKNOWN_ERROR;
216
217 ret = service_update(s, msg, tb);
218 if (ret)
219 goto free;
220
221 avl_insert(&services, &s->avl);
222
223 return 0;
224
225 free:
226 free(msg);
227 return ret;
228 }
229
230 static int
231 service_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
232 struct ubus_request_data *req, const char *method,
233 struct blob_attr *msg)
234 {
235 struct service *s;
236
237 blob_buf_init(&b, 0);
238 avl_for_each_element(&services, s, avl) {
239 void *c;
240
241 c = blobmsg_open_table(&b, s->name);
242 blobmsg_close_table(&b, c);
243 }
244
245 ubus_send_reply(ctx, req, b.head);
246
247 return 0;
248 }
249
250 enum {
251 SERVICE_DEL_NAME,
252 __SERVICE_DEL_MAX,
253 };
254
255 static const struct blobmsg_policy service_del_attrs[__SERVICE_DEL_MAX] = {
256 [SERVICE_DEL_NAME] = { "name", BLOBMSG_TYPE_STRING },
257 };
258
259
260 static int
261 service_handle_delete(struct ubus_context *ctx, struct ubus_object *obj,
262 struct ubus_request_data *req, const char *method,
263 struct blob_attr *msg)
264 {
265 struct blob_attr *tb[__SERVICE_DEL_MAX], *cur;
266 struct service *s, *tmp;
267
268 blobmsg_parse(service_del_attrs, __SERVICE_DEL_MAX, tb, blob_data(msg), blob_len(msg));
269
270 cur = tb[SERVICE_ATTR_NAME];
271 if (!cur) {
272 avl_for_each_element_safe(&services, s, avl, tmp)
273 service_delete(s);
274 return 0;
275 }
276
277 s = avl_find_element(&services, blobmsg_data(cur), s, avl);
278 if (!s)
279 return UBUS_STATUS_NOT_FOUND;
280
281 service_delete(s);
282 return 0;
283 }
284
285 static struct ubus_method main_object_methods[] = {
286 { .name = "list", .handler = service_handle_list },
287 { .name = "set", .handler = service_handle_set },
288 { .name = "delete", .handler = service_handle_delete },
289 };
290
291 static struct ubus_object_type main_object_type =
292 UBUS_OBJECT_TYPE("service", main_object_methods);
293
294 static struct ubus_object main_object = {
295 .name = "service",
296 .type = &main_object_type,
297 .methods = main_object_methods,
298 .n_methods = ARRAY_SIZE(main_object_methods),
299 };
300
301 void procd_init_service(struct ubus_context *ctx)
302 {
303 avl_init(&services, avl_strcmp, false, NULL);
304 ubus_add_object(ctx, &main_object);
305 }