Cherry-picks the code from 0.1.x branch
[rahunas] / src / rh-task-bandwidth.c
1 /**
2  * RahuNAS task bandwidth implementation 
3  * Author: Neutron Soutmun <neo.neutron@gmail.com>
4  * Date:   2008-11-20
5  */
6
7 #include <stdlib.h>
8 #include <string.h>
9 #include <syslog.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <sys/wait.h>
13 #include <sys/stat.h>
14 #include <fcntl.h>
15
16 #include "rahunasd.h"
17 #include "rh-task.h"
18 #include "rh-task-bandwidth.h"
19 #include "rh-task-memset.h"
20 #include "rh-utils.h"
21
22 static unsigned short slot_flags[MAX_SLOT_PAGE] = {1};
23 static unsigned short slot_count = 0;
24
25 unsigned short _get_slot_id()
26 {
27   unsigned short slot_id    = 0;
28   unsigned short page       = 0;
29   unsigned char  id_on_page = 0;
30   time_t random_time;
31
32   // Slot is full 
33   if (slot_count >= MAX_SLOT_ID)
34     return 0;
35
36   // Do a random slot_id 
37   while (slot_id == 0) {
38     time(&(random_time));
39     srandom(random_time);
40     slot_id = random()/(int)(((unsigned int)RAND_MAX + 1) / (MAX_SLOT_ID + 1));
41    
42     // Check validity
43     page = slot_id / PAGE_SIZE; 
44     id_on_page = slot_id % PAGE_SIZE;
45    
46     if (!(slot_flags[page] & (1 << id_on_page))) {
47       // Slot available
48       break;
49     }
50
51     // Slot not available, retry
52     slot_id = 0;
53   }
54
55   return slot_id;
56 }
57
58 void mark_reserved_slot_id(unsigned int slot_id)
59 {
60   unsigned short page       = 0;
61   unsigned char  id_on_page = 0;
62
63   page = slot_id / PAGE_SIZE; 
64   id_on_page = slot_id % PAGE_SIZE;
65
66   slot_count++;
67   slot_flags[page] |= 1 << id_on_page;
68 }
69
70 int bandwidth_exec(RHVServer *vs, char *const args[])
71 {
72   pid_t ws;
73   pid_t pid;
74   int status;
75   int exec_pipe[2];
76   char buffer[150];
77   char *endline = NULL;
78   int ret = 0;
79   int fd = 0;
80   
81   memset(buffer, '\0', sizeof(buffer));
82
83   if (pipe(exec_pipe) == -1) {
84     logmsg(RH_LOG_ERROR, "Error: pipe()");
85     return -1;
86   }
87   DP("pipe0=%d,pipe1=%d", exec_pipe[0], exec_pipe[1]);
88
89   pid = vfork();
90   dup2(exec_pipe[1], STDOUT_FILENO);
91
92   if (pid == 0) {
93     // Child
94     execv(RAHUNAS_BANDWIDTH_WRAPPER, args);
95   } else if (pid < 0) {
96     // Fork error
97     logmsg(RH_LOG_ERROR, "Error: vfork()"); 
98     ret = -1;
99   } else {
100     // Parent
101     ws = waitpid(pid, &status, 0);
102
103     DP("Bandwidth: Return (%d)", WEXITSTATUS(status));
104
105     // Return message log
106     DP("Read message");
107     read(exec_pipe[0], buffer, sizeof(buffer));
108
109     if (buffer != NULL) {
110       DP("Got message: %s", buffer);
111       endline = strstr(buffer, "\n");
112       if (endline != NULL) 
113         *endline = '\0';
114
115       if (vs != NULL) {
116         logmsg(RH_LOG_NORMAL, "[%s] Bandwidth: %s", 
117           vs->vserver_config->vserver_name, buffer);
118       } else {
119         logmsg(RH_LOG_NORMAL, "[main server] Bandwidth: %s", buffer);
120       }
121     }
122
123     if (WIFEXITED(status)) {
124       ret = WEXITSTATUS(status);
125     } else {
126       ret = -1;
127     } 
128   }
129
130   if ((buffer != NULL) && (strncmp (buffer, "NOT COMPLETED", 13) == 0))
131     ret = -2;  // Not complete need to retry
132
133   close(exec_pipe[0]);
134   close(exec_pipe[1]);
135   return ret;
136 }
137
138 int bandwidth_start(RHVServer *vs)
139 {
140   char *args[5];
141   struct interfaces *iface = vs->vserver_config->iface;
142   int  ret;
143
144   DP("Bandwidth: start");
145
146   args[0] = RAHUNAS_BANDWIDTH_WRAPPER;
147   args[1] = "start";
148   args[2] = iface->dev_internal;
149   args[3] = iface->dev_ifb;
150   args[4] = (char *) 0;
151
152   ret = bandwidth_exec(vs, args);
153   return ret; 
154 }
155
156 int bandwidth_stop(RHVServer *vs)
157 {
158   char *args[5];
159   struct interfaces *iface = vs->vserver_config->iface;
160   int  ret;
161
162   DP("Bandwidth: stop");
163
164   args[0] = RAHUNAS_BANDWIDTH_WRAPPER;
165   args[1] = "stop";
166   args[2] = iface->dev_internal;
167   args[3] = iface->dev_ifb;
168   args[4] = (char *) 0;
169
170   ret = bandwidth_exec(vs, args);
171   return ret; 
172 }
173
174 int bandwidth_add(RHVServer *vs, struct bandwidth_req *bw_req)
175 {
176   char *args[9];
177   struct interfaces *iface = vs->vserver_config->iface;
178
179   DP("Bandwidth: request %s %s %s %s", bw_req->slot_id, 
180      bw_req->ip, bw_req->bandwidth_max_down, bw_req->bandwidth_max_up);
181
182   args[0] = RAHUNAS_BANDWIDTH_WRAPPER;
183   args[1] = "add";
184   args[2] = bw_req->slot_id;
185   args[3] = bw_req->ip;
186   args[4] = bw_req->bandwidth_max_down;
187   args[5] = bw_req->bandwidth_max_up;
188   args[6] = iface->dev_internal;
189   args[7] = iface->dev_ifb;
190   args[8] = (char *) 0;
191
192   return bandwidth_exec(vs, args);
193 }
194
195 int bandwidth_del(RHVServer *vs, struct bandwidth_req *bw_req)
196 {
197   char *args[6];
198   struct interfaces *iface = vs->vserver_config->iface;
199
200   DP("Bandwidth: request %s", bw_req->slot_id);
201
202   args[0] = RAHUNAS_BANDWIDTH_WRAPPER;
203   args[1] = "del";
204   args[2] = bw_req->slot_id;
205   args[3] = iface->dev_internal;
206   args[4] = iface->dev_ifb;
207   args[5] = (char *) 0;
208
209   return bandwidth_exec(vs, args);
210 }
211
212 /* Start service task */
213 static int startservice (void)
214 {
215   /* Do nothing */
216 }
217
218 /* Stop service task */
219 static int stopservice (void)
220 {
221   /* Do nothing */
222 }
223
224 /* Initialize */
225 static void init (RHVServer *vs)
226 {
227   struct interfaces *iface = NULL;
228   if (!vs)
229     return;
230
231   if (vs->vserver_config->init_flag == VS_RELOAD)
232     goto initial;
233
234   interfaces_list = append_interface (interfaces_list, 
235                                       vs->vserver_config->dev_internal);
236
237 initial:
238   vs->vserver_config->iface = get_interface (interfaces_list, 
239                                              vs->vserver_config->dev_internal);
240   iface = vs->vserver_config->iface;
241   if (!iface->init)
242     if (bandwidth_start(vs) >= 0)
243       iface->init = 1;
244 }
245
246 /* Cleanup */
247 static void cleanup (RHVServer *vs)
248 {
249   struct interfaces *iface = NULL;
250   if (!vs)
251     return;
252
253   if (vs->vserver_config->init_flag == VS_RELOAD)
254     return;
255
256   iface = vs->vserver_config->iface;
257   DP ("Bandwidth Cleanup: init=%d, hit=%d", iface->init, iface->hit);
258   if (iface->init && iface->hit <= 1)
259     if (bandwidth_stop(vs) >= 0)
260       iface->init = 0;
261
262   interfaces_list = remove_interface (interfaces_list, 
263                                       vs->vserver_config->dev_internal);
264 }
265
266 /* Start session task */
267 static int startsess (RHVServer *vs, struct task_req *req)
268 {
269   struct bandwidth_req bw_req;
270   unsigned short slot_id;
271   unsigned char max_try = 3;
272   GList *member_node = NULL;
273   struct rahunas_member *member = NULL;
274
275   member_node = member_get_node_by_id(vs, req->id);
276   if (member_node == NULL)
277     return (-1);
278
279   member = (struct rahunas_member *) member_node->data;
280
281   if (member->bandwidth_max_down == 0 && member->bandwidth_max_up == 0)
282     return 0;
283
284   if (member->bandwidth_slot_id > 0)
285     return 0;
286
287   memset (bw_req.ip, 0, sizeof (bw_req.ip));
288   memset (bw_req.bandwidth_max_down, 0, sizeof (bw_req.bandwidth_max_down));
289   memset (bw_req.bandwidth_max_up, 0, sizeof (bw_req.bandwidth_max_up));
290   
291   // Formating the bandwidth request
292   snprintf(bw_req.ip, sizeof (bw_req.ip), "%s", idtoip(vs->v_map, req->id));
293   snprintf(bw_req.bandwidth_max_down, sizeof (bw_req.bandwidth_max_down), 
294            "%lu", member->bandwidth_max_down);
295   snprintf(bw_req.bandwidth_max_up, sizeof (bw_req.bandwidth_max_up), "%lu", 
296            member->bandwidth_max_up);
297   
298   while (max_try > 0) { 
299     slot_id = _get_slot_id();
300     snprintf(bw_req.slot_id, sizeof (bw_req.slot_id), "%d", slot_id);
301     if (bandwidth_add(vs, &bw_req) == 0)
302       break;
303     else {
304       max_try--;
305       sleep (1); 
306     }
307   }
308
309
310   if (max_try == 0) {
311     logmsg(RH_LOG_ERROR, "[%s] Bandwidth: Maximum trying, failed!",
312            vs->vserver_config->vserver_name);
313     return -1;
314   }
315
316   mark_reserved_slot_id(slot_id);
317   member->bandwidth_slot_id = slot_id;
318  
319   return 0;
320 }
321
322 /* Stop session task */
323 static int stopsess  (RHVServer *vs, struct task_req *req)
324 {
325   struct bandwidth_req bw_req;
326   unsigned short slot_id = 0;
327   GList *member_node = NULL;
328   struct rahunas_member *member = NULL;
329   
330   member_node = member_get_node_by_id(vs, req->id);
331   if (member_node == NULL)
332     return (-1); 
333   
334   member = (struct rahunas_member *) member_node->data;
335   slot_id = member->bandwidth_slot_id;
336
337   if (slot_id < 1)
338     return 0;
339
340   snprintf(bw_req.slot_id, sizeof (bw_req.slot_id), "%d", slot_id);
341
342   if (bandwidth_del(vs, &bw_req) == 0) {
343     member->bandwidth_slot_id = 0;
344
345     if (slot_count > 0)
346       slot_count--;
347   
348     return 0; 
349   } else {
350     return -1;
351   }
352 }
353
354 /* Commit start session task */
355 static int commitstartsess (RHVServer *vs, struct task_req *req)
356 {
357   /* Do nothing or need to implement */
358 }
359
360 /* Commit stop session task */
361 static int commitstopsess  (RHVServer *vs, struct task_req *req)
362 {
363   /* Do nothing or need to implement */
364 }
365
366 /* Rollback start session task */
367 static int rollbackstartsess (RHVServer *vs, struct task_req *req)
368 {
369   /* Do nothing or need to implement */
370 }
371
372 /* Rollback stop session task */
373 static int rollbackstopsess  (RHVServer *vs, struct task_req *req)
374 {
375   /* Do nothing or need to implement */
376 }
377
378 static struct task taskbandwidth = {
379   .taskname = "BANDWIDTH",
380   .taskprio = 20,
381   .init = &init,
382   .cleanup = &cleanup,
383   .startservice = &startservice,
384   .stopservice = &stopservice,
385   .startsess = &startsess,
386   .stopsess = &stopsess,
387   .commitstartsess = &commitstartsess,
388   .commitstopsess = &commitstopsess,
389   .rollbackstartsess = &rollbackstartsess,
390   .rollbackstopsess = &rollbackstopsess,
391 };
392
393 void rh_task_bandwidth_reg(RHMainServer *ms) {
394   task_register(ms, &taskbandwidth);
395 }