a25f91a914b252f45e858fd90db26cce9fbf3419
[project/procd.git] / instance.c
1 #include <sys/resource.h>
2 #include <sys/types.h>
3 #include <sys/socket.h>
4 #include <net/if.h>
5 #include <unistd.h>
6
7 #include "procd.h"
8 #include "service.h"
9 #include "instance.h"
10
11 enum {
12         INSTANCE_ATTR_COMMAND,
13         INSTANCE_ATTR_ENV,
14         INSTANCE_ATTR_DATA,
15         INSTANCE_ATTR_NETDEV,
16         INSTANCE_ATTR_NICE,
17         __INSTANCE_ATTR_MAX
18 };
19
20 static const struct blobmsg_policy instance_attr[__INSTANCE_ATTR_MAX] = {
21         [INSTANCE_ATTR_COMMAND] = { "command", BLOBMSG_TYPE_ARRAY },
22         [INSTANCE_ATTR_ENV] = { "env", BLOBMSG_TYPE_TABLE },
23         [INSTANCE_ATTR_DATA] = { "data", BLOBMSG_TYPE_TABLE },
24         [INSTANCE_ATTR_NETDEV] = { "netdev", BLOBMSG_TYPE_ARRAY },
25         [INSTANCE_ATTR_NICE] = { "nice", BLOBMSG_TYPE_INT32 },
26 };
27
28 struct instance_netdev {
29         struct blobmsg_list_node node;
30         int ifindex;
31 };
32
33 static void
34 instance_run(struct service_instance *in)
35 {
36         struct blobmsg_list_node *var;
37         struct blob_attr *cur;
38         char **argv;
39         int argc = 1; /* NULL terminated */
40         int rem;
41
42         if (in->nice)
43                 setpriority(PRIO_PROCESS, 0, in->nice);
44
45         blobmsg_for_each_attr(cur, in->command, rem)
46                 argc++;
47
48         blobmsg_list_for_each(&in->env, var)
49                 setenv(blobmsg_name(var->data), blobmsg_data(var->data), 1);
50
51         argv = alloca(sizeof(char *) * argc);
52         argc = 0;
53
54         blobmsg_for_each_attr(cur, in->command, rem)
55                 argv[argc++] = blobmsg_data(cur);
56
57         argv[argc] = NULL;
58         execvp(argv[0], argv);
59         exit(127);
60 }
61
62 void
63 instance_start(struct service_instance *in)
64 {
65         int pid;
66
67         if (in->proc.pending)
68                 return;
69
70         in->restart = false;
71         if (!in->valid)
72                 return;
73
74         pid = fork();
75         if (pid < 0)
76                 return;
77
78         if (!pid) {
79                 instance_run(in);
80                 return;
81         }
82
83         DPRINTF("Started instance %s::%s\n", in->srv->name, in->name);
84         in->proc.pid = pid;
85         uloop_process_add(&in->proc);
86 }
87
88 static void
89 instance_timeout(struct uloop_timeout *t)
90 {
91         struct service_instance *in;
92
93         in = container_of(t, struct service_instance, timeout);
94         kill(in->proc.pid, SIGKILL);
95         uloop_process_delete(&in->proc);
96         in->proc.cb(&in->proc, -1);
97 }
98
99 static void
100 instance_exit(struct uloop_process *p, int ret)
101 {
102         struct service_instance *in;
103
104         in = container_of(p, struct service_instance, proc);
105         DPRINTF("Instance %s::%s exit with error code %d\n", in->srv->name, in->name, ret);
106         uloop_timeout_cancel(&in->timeout);
107         if (in->restart)
108                 instance_start(in);
109 }
110
111 void
112 instance_stop(struct service_instance *in, bool restart)
113 {
114         if (!in->proc.pending)
115                 return;
116
117         kill(in->proc.pid, SIGTERM);
118 }
119
120 static bool
121 instance_config_changed(struct service_instance *in, struct service_instance *in_new)
122 {
123         if (!in->valid)
124                 return true;
125
126         if (!blob_attr_equal(in->command, in_new->command))
127                 return true;
128
129         if (!blobmsg_list_equal(&in->env, &in_new->env))
130                 return true;
131
132         if (!blobmsg_list_equal(&in->data, &in_new->data))
133                 return true;
134
135         if (!blobmsg_list_equal(&in->netdev, &in_new->netdev))
136                 return true;
137
138         if (in->nice != in_new->nice)
139                 return true;
140
141         return false;
142 }
143
144 static bool
145 instance_netdev_cmp(struct blobmsg_list_node *l1, struct blobmsg_list_node *l2)
146 {
147         struct instance_netdev *n1 = container_of(l1, struct instance_netdev, node);
148         struct instance_netdev *n2 = container_of(l2, struct instance_netdev, node);
149
150         return n1->ifindex == n2->ifindex;
151 }
152
153 static void
154 instance_netdev_update(struct blobmsg_list_node *l)
155 {
156         struct instance_netdev *n = container_of(l, struct instance_netdev, node);
157
158         n->ifindex = if_nametoindex(n->node.avl.key);
159 }
160
161 static bool
162 instance_config_parse(struct service_instance *in)
163 {
164         struct blob_attr *tb[__INSTANCE_ATTR_MAX];
165         struct blob_attr *cur, *cur2;
166         int argc = 0;
167         int rem;
168
169         blobmsg_parse(instance_attr, __INSTANCE_ATTR_MAX, tb,
170                 blobmsg_data(in->config), blobmsg_data_len(in->config));
171
172         cur = tb[INSTANCE_ATTR_COMMAND];
173         if (!cur)
174                 return false;
175
176         if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING))
177                 return false;
178
179         blobmsg_for_each_attr(cur2, cur, rem) {
180                 argc++;
181                 break;
182         }
183         if (!argc)
184                 return false;
185
186         in->command = cur;
187
188         if ((cur = tb[INSTANCE_ATTR_NICE])) {
189                 in->nice = (int8_t) blobmsg_get_u32(cur);
190                 if (in->nice < -20 || in->nice > 20)
191                         return false;
192         }
193
194         if ((cur = tb[INSTANCE_ATTR_ENV])) {
195                 if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING))
196                         return false;
197
198                 blobmsg_list_fill(&in->env, blobmsg_data(cur), blobmsg_data_len(cur), false);
199         }
200
201         if ((cur = tb[INSTANCE_ATTR_DATA])) {
202                 if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING))
203                         return false;
204
205                 blobmsg_list_fill(&in->data, blobmsg_data(cur), blobmsg_data_len(cur), false);
206         }
207
208         if ((cur = tb[INSTANCE_ATTR_NETDEV])) {
209                 struct blobmsg_list_node *ndev;
210
211                 if (!blobmsg_check_attr_list(cur, BLOBMSG_TYPE_STRING))
212                         return false;
213
214                 blobmsg_list_fill(&in->netdev, blobmsg_data(cur), blobmsg_data_len(cur), true);
215                 blobmsg_list_for_each(&in->netdev, ndev)
216                         instance_netdev_update(ndev);
217         }
218
219         return true;
220 }
221
222 static void
223 instance_config_cleanup(struct service_instance *in)
224 {
225         blobmsg_list_free(&in->env);
226         blobmsg_list_free(&in->data);
227         blobmsg_list_free(&in->netdev);
228 }
229
230 static void
231 instance_config_move(struct service_instance *in, struct service_instance *in_src)
232 {
233         instance_config_cleanup(in);
234         blobmsg_list_move(&in->env, &in_src->env);
235         blobmsg_list_move(&in->data, &in_src->data);
236         blobmsg_list_move(&in->netdev, &in_src->netdev);
237         in->command = in_src->command;
238         in->name = in_src->name;
239         in->node.avl.key = in_src->node.avl.key;
240
241         free(in->config);
242         in->config = in_src->config;
243         in_src->config = NULL;
244 }
245
246 bool
247 instance_update(struct service_instance *in, struct service_instance *in_new)
248 {
249         bool changed = instance_config_changed(in, in_new);
250
251         if (!changed)
252                 return false;
253
254         in->restart = true;
255         instance_stop(in, true);
256         instance_config_move(in, in_new);
257         return true;
258 }
259
260 void
261 instance_free(struct service_instance *in)
262 {
263         uloop_process_delete(&in->proc);
264         uloop_timeout_cancel(&in->timeout);
265         instance_config_cleanup(in);
266         free(in->config);
267         free(in);
268 }
269
270 void
271 instance_init(struct service_instance *in, struct service *s, struct blob_attr *config)
272 {
273         config = blob_memdup(config);
274         in->srv = s;
275         in->name = blobmsg_name(config);
276         in->config = config;
277         in->timeout.cb = instance_timeout;
278         in->proc.cb = instance_exit;
279
280         blobmsg_list_init(&in->netdev, struct instance_netdev, node, instance_netdev_cmp);
281         blobmsg_list_simple_init(&in->env);
282         blobmsg_list_simple_init(&in->data);
283         in->valid = instance_config_parse(in);
284 }
285
286 void instance_dump(struct blob_buf *b, struct service_instance *in)
287 {
288         void *i;
289
290         i = blobmsg_open_table(b, in->name);
291         blobmsg_add_u8(b, "running", in->proc.pending);
292         if (in->proc.pending)
293                 blobmsg_add_u32(b, "pid", in->proc.pid);
294         blobmsg_add_blob(b, in->command);
295         blobmsg_close_table(b, i);
296 }