unet-cli: strip initial newline in usage message
[project/unetd.git] / service.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <libubox/avl-cmp.h>
6 #include "unetd.h"
7
8 enum {
9 SERVICE_ATTR_TYPE,
10 SERVICE_ATTR_CONFIG,
11 SERVICE_ATTR_MEMBERS,
12 __SERVICE_ATTR_MAX
13 };
14
15 static const struct blobmsg_policy service_policy[__SERVICE_ATTR_MAX] = {
16 [SERVICE_ATTR_TYPE] = { "type", BLOBMSG_TYPE_STRING },
17 [SERVICE_ATTR_CONFIG] = { "config", BLOBMSG_TYPE_TABLE },
18 [SERVICE_ATTR_MEMBERS] = { "members", BLOBMSG_TYPE_ARRAY },
19 };
20
21 void network_services_free(struct network *net)
22 {
23 vlist_flush_all(&net->services);
24 }
25
26 static int
27 __service_add_member(struct network_host **list, int *n, struct network_host *member)
28 {
29 int i;
30
31 for (i = 0; i < *n; i++) {
32 if (list[i] == member)
33 return 0;
34 }
35
36 list[(*n)++] = member;
37 return 1;
38 }
39
40 static int
41 __service_add_group(struct network_host **list, int *n, struct network_group *group)
42 {
43 int i, count = 0;
44
45 for (i = 0; i < group->n_members; i++)
46 count += __service_add_member(list, n, group->members[i]);
47
48 return count;
49 }
50
51 static int
52 __service_parse_members(struct network *net, struct network_service *s,
53 const char *name)
54 {
55 struct network_group *group;
56 struct network_host *host;
57
58 if (name[0] != '@') {
59 host = avl_find_element(&net->hosts, name, host, node);
60
61 if (!host)
62 return 0;
63
64 if (s)
65 __service_add_member(s->members, &s->n_members, host);
66
67 return 1;
68 }
69
70 name++;
71 group = avl_find_element(&net->groups, name, group, node);
72 if (!group)
73 return 0;
74
75 if (s)
76 return __service_add_group(s->members, &s->n_members, group);
77 else
78 return group->n_members;
79 }
80
81 static int
82 service_parse_members(struct network *net, struct network_service *s,
83 struct blob_attr *data)
84 {
85 struct blob_attr *cur;
86 int rem;
87 int n = 0;
88
89 blobmsg_for_each_attr(cur, data, rem)
90 n += __service_parse_members(net, s, blobmsg_get_string(cur));
91
92 return n;
93 }
94
95 static void
96 service_add(struct network *net, struct blob_attr *data)
97 {
98 struct network_service *s;
99 struct blob_attr *tb[__SERVICE_ATTR_MAX];
100 struct blob_attr *cur, *config;
101 const char *name = blobmsg_name(data);
102 const char *type = NULL;
103 char *name_buf, *type_buf;
104 void *config_buf;
105 int n_members;
106
107 blobmsg_parse(service_policy, __SERVICE_ATTR_MAX, tb,
108 blobmsg_data(data), blobmsg_len(data));
109
110 if ((cur = tb[SERVICE_ATTR_TYPE]) != NULL)
111 type = blobmsg_get_string(cur);
112
113 if (blobmsg_check_array(tb[SERVICE_ATTR_MEMBERS], BLOBMSG_TYPE_STRING) < 0)
114 return;
115
116 config = tb[SERVICE_ATTR_CONFIG];
117
118 n_members = service_parse_members(net, NULL, tb[SERVICE_ATTR_MEMBERS]);
119 s = calloc_a(sizeof(*s) + n_members * sizeof(s->members[0]),
120 &name_buf, strlen(name) + 1,
121 &type_buf, type ? strlen(type) + 1 : 0,
122 &config_buf, config ? blob_pad_len(config) : 0);
123
124 strcpy(name_buf, name);
125 if (type)
126 s->type = strcpy(type_buf, type);
127 if (config)
128 s->config = memcpy(config_buf, config, blob_pad_len(config));
129 #ifdef VXLAN_SUPPORT
130 if (type && !strcmp(type, "vxlan"))
131 s->ops = &vxlan_ops;
132 #endif
133
134 service_parse_members(net, s, tb[SERVICE_ATTR_MEMBERS]);
135 vlist_add(&net->services, &s->node, name_buf);
136 }
137
138 void network_services_add(struct network *net, struct blob_attr *data)
139 {
140 struct blob_attr *cur;
141 int rem;
142
143 blobmsg_for_each_attr(cur, data, rem)
144 service_add(net, cur);
145 }
146
147 static void
148 service_update(struct vlist_tree *tree, struct vlist_node *node_new,
149 struct vlist_node *node_old)
150 {
151 struct network *net = container_of(tree, struct network, services);
152 struct network_service *s_old, *s_new;
153
154 s_new = container_of_safe(node_new, struct network_service, node);
155 s_old = container_of_safe(node_old, struct network_service, node);
156
157 if (s_new && s_old && s_new->ops && s_new->ops == s_old->ops) {
158 s_new->ops->init(net, s_new, s_old);
159 goto out;
160 }
161
162 if (s_new && s_new->ops)
163 s_new->ops->init(net, s_new, NULL);
164
165 if (s_old && s_old->ops)
166 s_old->ops->free(net, s_old);
167
168 out:
169 free(s_old);
170 }
171
172 void network_services_peer_update(struct network *net, struct network_peer *peer)
173 {
174 struct network_service *s;
175
176 vlist_for_each_element(&net->services, s, node) {
177 if (!s->ops || !s->ops->peer_update)
178 continue;
179
180 s->ops->peer_update(net, s, peer);
181 }
182 }
183
184 void network_services_init(struct network *net)
185 {
186 vlist_init(&net->services, avl_strcmp, service_update);
187 }