]> jfr.im git - solanum.git/blob - ircd/batch.c
Implement the netsplit batch type.
[solanum.git] / ircd / batch.c
1 /* ircd/batch.c - batch management
2 * Copyright (c) 2016 Elizabeth Myers <elizabeth@interlinked.me>
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice is present in all copies.
7 *
8 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
9 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
11 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
12 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
13 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
14 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
15 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
16 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
17 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
18 * POSSIBILITY OF SUCH DAMAGE.
19 */
20
21 #include "stdinc.h"
22 #include "batch.h"
23 #include "client.h"
24 #include "s_serv.h"
25 #include "send.h"
26 #include "channel.h"
27 #include "hash.h"
28 #include "s_assert.h"
29 #include "rb_radixtree.h"
30
31 /* Multiple batches may be in progress for each slot. */
32 rb_dlink_list batches[BATCH_LAST];
33
34 static inline void
35 generate_batch_id(char *ptr, size_t len)
36 {
37 size_t i;
38 const char batchchars[65] =
39 "\0._0123456789" /* Zero-indexed */
40 "abcdefghijklmnopqrstuvwxyz"
41 "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
42
43 len--; /* Room for \0 */
44 for(i = 0; i < len; i++)
45 {
46 char r;
47
48 do
49 {
50 r = batchchars[rand() & 0x7F]; /* random int between 0-64 */
51
52 if(r == '\0' && i > 3)
53 /* We have enough chars */
54 goto end;
55 } while(r == '\0');
56
57 ptr[i] = r;
58 }
59
60 end:
61 ptr[i] = '\0';
62 }
63
64 struct Batch *
65 start_batch(batch_type batch, void *data, int parc, ...)
66 {
67 struct Batch *batch_p = rb_malloc(sizeof(struct Batch));
68
69 batch_p->batch = batch;
70 generate_batch_id(batch_p->id, sizeof(batch_p->id));
71 batch_p->data = data;
72 batch_p->parc = parc;
73 if(parc > 0)
74 {
75 /* Get the argument list */
76 va_list args;
77
78 batch_p->parv = rb_malloc(sizeof(char *) * parc);
79
80 va_start(args, parc);
81 for(size_t i = 0; i < parc; i++)
82 batch_p->parv[i] = va_arg(args, char *);
83 va_end(args);
84 }
85
86 /* Batch-type specific processing */
87 switch(batch)
88 {
89 case BATCH_NETSPLIT:
90 case BATCH_NETJOIN:
91 {
92 /* Build list of channels affected by the batch */
93 rb_dlink_list *clist;
94 rb_radixtree_iteration_state iter;
95 struct Channel *chptr;
96
97 batch_p->pdata = clist = rb_malloc(sizeof(rb_dlink_list));
98
99 /* Look for channels we need to send the batch to */
100 RB_RADIXTREE_FOREACH(chptr, &iter, channel_tree)
101 {
102 rb_dlink_node *ptr;
103
104 if(rb_dlink_list_length(&chptr->locmembers) == 0)
105 /* They're all remotes, so don't send a batch */
106 continue;
107
108 /* Hunt for members in the channel from the target server
109 * If we find one, send the channel a BATCH message */
110 RB_DLINK_FOREACH(ptr, chptr->members.head)
111 {
112 struct Client *client_p = ptr->data;
113
114 if(client_p->from == data)
115 {
116 rb_dlinkAddAlloc(rb_strdup(chptr->chname), clist);
117 sendto_channel_local_with_capability(ALL_MEMBERS, CLICAP_BATCH, NOCAPS,
118 chptr, ":%s BATCH +%s %s %s",
119 me.name,
120 batch == BATCH_NETSPLIT ? "netsplit" : "netjoin",
121 batch_p->parv[0], batch_p->parv[1]);
122 break;
123 }
124 }
125 }
126 }
127 break;
128 default:
129 s_assert(0);
130 break;
131 }
132
133 rb_dlinkAdd(batch_p, &batch_p->node, &batches[batch]);
134
135 return batch_p;
136 }
137
138 void
139 finish_batch(struct Batch *batch_p)
140 {
141 if(batch_p == NULL)
142 return;
143
144 /* Batch type-specific processing */
145 switch(batch_p->batch)
146 {
147 case BATCH_NETSPLIT:
148 case BATCH_NETJOIN:
149 {
150 rb_dlink_list *clist = batch_p->pdata;
151 rb_dlink_node *ptr, *nptr;
152
153 RB_DLINK_FOREACH_SAFE(ptr, nptr, clist->head)
154 {
155 struct Channel *chptr = find_channel(ptr->data);
156
157 if(chptr != NULL) /* Shouldn't be but just in case... */
158 {
159 sendto_channel_local_with_capability(ALL_MEMBERS, CLICAP_BATCH, NOCAPS,
160 chptr, ":%s BATCH -%s %s %s",
161 me.name,
162 batch_p->batch == BATCH_NETSPLIT ? "netsplit" : "netjoin",
163 batch_p->parv[0], batch_p->parv[1]);
164 }
165
166 rb_free(ptr->data);
167 rb_dlinkDestroy(ptr, clist);
168 }
169
170 rb_free(clist);
171 }
172 break;
173 default:
174 s_assert(0);
175 break;
176 }
177
178 /* Free all the strings */
179 for(size_t i = 0; i < (batch_p->parc - 1); i++)
180 rb_free(batch_p->parv[i]);
181
182 rb_free(batch_p->parv);
183
184 rb_dlinkDelete(&batch_p->node, &batches[batch_p->batch]);
185 rb_free(batch_p);
186 }
187
188 struct Batch *
189 find_batch(batch_type batch, void *data)
190 {
191 rb_dlink_node *ptr;
192
193 RB_DLINK_FOREACH(ptr, batches[batch].head)
194 {
195 struct Batch *batch_p = ptr->data;
196
197 if(batch_p->data == data)
198 return batch_p;
199 }
200
201 return NULL;
202 }