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