implement more parts of the service core api
[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 service_instance_update(struct vlist_tree *tree, struct vlist_node *node_new,
10                         struct vlist_node *node_old)
11 {
12         struct service_instance *in_o = NULL, *in_n = NULL;
13
14         if (node_old)
15                 in_o = container_of(node_old, struct service_instance, node);
16
17         if (node_new)
18                 in_n = container_of(node_new, struct service_instance, node);
19
20         do {
21                 if (!in_o || !in_n)
22                         break;
23
24                 /* full match, nothing to do */
25                 return;
26         } while (0);
27
28         if (in_o) {
29                 /* kill old process */
30                 free(in_o);
31         }
32
33         if (in_n) {
34                 /* start new process */
35         }
36 }
37
38 static void
39 service_instance_add(struct service *s, struct blob_attr *attr)
40 {
41         struct service_instance *in;
42         const char *name = blobmsg_name(attr);
43
44         if (blobmsg_type(attr) != BLOBMSG_TYPE_TABLE)
45                 return;
46
47         in = calloc(1, sizeof(*in));
48         if (!in)
49                 return;
50
51         in->config = attr;
52         vlist_add(&s->instances, &in->node, (void *) name);
53 }
54
55 static struct service *
56 service_alloc(const char *name)
57 {
58         struct service *s;
59
60         s = calloc(1, sizeof(*s));
61         vlist_init(&s->instances, avl_strcmp, service_instance_update);
62
63         return s;
64 }
65
66 enum {
67         SERVICE_ATTR_NAME,
68         SERVICE_ATTR_SCRIPT,
69         SERVICE_ATTR_INSTANCES,
70         __SERVICE_ATTR_MAX
71 };
72
73 static const struct blobmsg_policy service_attrs[__SERVICE_ATTR_MAX] = {
74         [SERVICE_ATTR_NAME] = { "name", BLOBMSG_TYPE_STRING },
75         [SERVICE_ATTR_SCRIPT] = { "script", BLOBMSG_TYPE_STRING },
76         [SERVICE_ATTR_INSTANCES] = { "instances", BLOBMSG_TYPE_TABLE },
77 };
78
79
80 static int
81 service_update(struct service *s, struct blob_attr *config, struct blob_attr **tb)
82 {
83         struct blob_attr *old_config = s->config;
84         struct blob_attr *cur;
85         int rem;
86
87         /* only the pointer changes, the content stays the same,
88          * no avl update necessary */
89         s->name = s->avl.key = blobmsg_data(tb[SERVICE_ATTR_NAME]);
90         s->config = config;
91
92         if (tb[SERVICE_ATTR_INSTANCES]) {
93                 vlist_update(&s->instances);
94                 blobmsg_for_each_attr(cur, tb[SERVICE_ATTR_INSTANCES], rem) {
95                         service_instance_add(s, cur);
96                 }
97                 vlist_flush(&s->instances);
98         }
99
100         free(old_config);
101
102         return 0;
103 }
104
105 static void
106 service_delete(struct service *s)
107 {
108         vlist_flush_all(&s->instances);
109         avl_delete(&services, &s->avl);
110         free(s->config);
111         free(s);
112 }
113
114 static int
115 service_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
116                    struct ubus_request_data *req, const char *method,
117                    struct blob_attr *msg)
118 {
119         struct blob_attr *tb[__SERVICE_ATTR_MAX], *cur;
120         struct service *s = NULL;
121         const char *name;
122         int ret = UBUS_STATUS_INVALID_ARGUMENT;
123
124         msg = blob_memdup(msg);
125         if (!msg)
126                 return UBUS_STATUS_UNKNOWN_ERROR;
127
128         blobmsg_parse(service_attrs, __SERVICE_ATTR_MAX, tb, blob_data(msg), blob_len(msg));
129         cur = tb[SERVICE_ATTR_NAME];
130         if (!cur)
131                 goto free;
132
133         name = blobmsg_data(cur);
134
135         s = avl_find_element(&services, name, s, avl);
136         if (s)
137                 return service_update(s, msg, tb);
138
139         s = service_alloc(name);
140         if (!s)
141                 return UBUS_STATUS_UNKNOWN_ERROR;
142
143         ret = service_update(s, msg, tb);
144         if (ret)
145                 goto free;
146
147         avl_insert(&services, &s->avl);
148
149         return 0;
150
151 free:
152         free(msg);
153         return ret;
154 }
155
156 static int
157 service_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
158                     struct ubus_request_data *req, const char *method,
159                     struct blob_attr *msg)
160 {
161         struct service *s;
162
163         blob_buf_init(&b, 0);
164         avl_for_each_element(&services, s, avl) {
165                 void *c;
166
167                 c = blobmsg_open_table(&b, s->name);
168                 blobmsg_close_table(&b, c);
169         }
170
171         ubus_send_reply(ctx, req, b.head);
172
173         return 0;
174 }
175
176 enum {
177         SERVICE_DEL_NAME,
178         __SERVICE_DEL_MAX,
179 };
180
181 static const struct blobmsg_policy service_del_attrs[__SERVICE_DEL_MAX] = {
182         [SERVICE_DEL_NAME] = { "name", BLOBMSG_TYPE_STRING },
183 };
184
185
186 static int
187 service_handle_delete(struct ubus_context *ctx, struct ubus_object *obj,
188                     struct ubus_request_data *req, const char *method,
189                     struct blob_attr *msg)
190 {
191         struct blob_attr *tb[__SERVICE_DEL_MAX], *cur;
192         struct service *s, *tmp;
193
194         blobmsg_parse(service_del_attrs, __SERVICE_DEL_MAX, tb, blob_data(msg), blob_len(msg));
195
196         cur = tb[SERVICE_ATTR_NAME];
197         if (!cur) {
198                 avl_for_each_element_safe(&services, s, avl, tmp)
199                         service_delete(s);
200                 return 0;
201         }
202
203         s = avl_find_element(&services, blobmsg_data(cur), s, avl);
204         if (!s)
205                 return UBUS_STATUS_NOT_FOUND;
206
207         service_delete(s);
208         return 0;
209 }
210
211 static struct ubus_method main_object_methods[] = {
212         { .name = "list", .handler = service_handle_list },
213         { .name = "set", .handler = service_handle_set },
214         { .name = "delete", .handler = service_handle_delete },
215 };
216
217 static struct ubus_object_type main_object_type =
218         UBUS_OBJECT_TYPE("service", main_object_methods);
219
220 static struct ubus_object main_object = {
221         .name = "service",
222         .type = &main_object_type,
223         .methods = main_object_methods,
224         .n_methods = ARRAY_SIZE(main_object_methods),
225 };
226
227 void procd_init_service(struct ubus_context *ctx)
228 {
229         avl_init(&services, avl_strcmp, false, NULL);
230         ubus_add_object(ctx, &main_object);
231 }