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