e147c27100ad17998a131f4b6fd0ac3a69260553
[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   close(exec_pipe[0]);
130   close(exec_pipe[1]);
131   return ret;
132 }
133
134 int bandwidth_start(struct vserver *vs)
135 {
136   char *args[5];
137   struct interfaces *iface = vs->vserver_config->iface;
138   int  ret;
139
140   DP("Bandwidth: start");
141
142   args[0] = RAHUNAS_BANDWIDTH_WRAPPER;
143   args[1] = "start";
144   args[2] = iface->dev_internal;
145   args[3] = iface->dev_ifb;
146   args[4] = (char *) 0;
147
148   ret = bandwidth_exec(vs, args);
149   return ret; 
150 }
151
152 int bandwidth_stop(struct vserver *vs)
153 {
154   char *args[5];
155   struct interfaces *iface = vs->vserver_config->iface;
156   int  ret;
157
158   DP("Bandwidth: stop");
159
160   args[0] = RAHUNAS_BANDWIDTH_WRAPPER;
161   args[1] = "stop";
162   args[2] = iface->dev_internal;
163   args[3] = iface->dev_ifb;
164   args[4] = (char *) 0;
165
166   ret = bandwidth_exec(vs, args);
167   return ret; 
168 }
169
170 int bandwidth_add(struct vserver *vs, struct bandwidth_req *bw_req)
171 {
172   char *args[9];
173   struct interfaces *iface = vs->vserver_config->iface;
174
175   DP("Bandwidth: request %s %s %s %s", bw_req->slot_id, 
176      bw_req->ip, bw_req->bandwidth_max_down, bw_req->bandwidth_max_up);
177
178   args[0] = RAHUNAS_BANDWIDTH_WRAPPER;
179   args[1] = "add";
180   args[2] = bw_req->slot_id;
181   args[3] = bw_req->ip;
182   args[4] = bw_req->bandwidth_max_down;
183   args[5] = bw_req->bandwidth_max_up;
184   args[6] = iface->dev_internal;
185   args[7] = iface->dev_ifb;
186   args[8] = (char *) 0;
187
188   return bandwidth_exec(vs, args);
189 }
190
191 int bandwidth_del(struct vserver *vs, struct bandwidth_req *bw_req)
192 {
193   char *args[6];
194   struct interfaces *iface = vs->vserver_config->iface;
195
196   DP("Bandwidth: request %s", bw_req->slot_id);
197
198   args[0] = RAHUNAS_BANDWIDTH_WRAPPER;
199   args[1] = "del";
200   args[2] = bw_req->slot_id;
201   args[3] = iface->dev_internal;
202   args[4] = iface->dev_ifb;
203   args[5] = (char *) 0;
204
205   return bandwidth_exec(vs, args);
206 }
207
208 /* Start service task */
209 static int startservice (void)
210 {
211   /* Do nothing */
212 }
213
214 /* Stop service task */
215 static int stopservice (void)
216 {
217   /* Do nothing */
218 }
219
220 /* Initialize */
221 static void init (struct vserver *vs)
222 {
223   struct interfaces *iface = NULL;
224   if (!vs)
225     return;
226
227   if (vs->vserver_config->init_flag == VS_RELOAD)
228     goto initial;
229
230   interfaces_list = append_interface (interfaces_list, 
231                                       vs->vserver_config->dev_internal);
232
233 initial:
234   vs->vserver_config->iface = get_interface (interfaces_list, 
235                                              vs->vserver_config->dev_internal);
236   iface = vs->vserver_config->iface;
237   if (!iface->init)
238     if (bandwidth_start(vs) >= 0)
239       iface->init = 1;
240 }
241
242 /* Cleanup */
243 static int cleanup (struct vserver *vs)
244 {
245   struct interfaces *iface = NULL;
246   if (!vs)
247     return;
248
249   if (vs->vserver_config->init_flag == VS_RELOAD)
250     return;
251
252   iface = vs->vserver_config->iface;
253   DP ("Bandwidth Cleanup: init=%d, hit=%d", iface->init, iface->hit);
254   if (iface->init && iface->hit <= 1)
255     if (bandwidth_stop(vs) >= 0)
256       iface->init = 0;
257
258   interfaces_list = remove_interface (interfaces_list, 
259                                       vs->vserver_config->dev_internal);
260 }
261
262 /* Start session task */
263 static int startsess (struct vserver *vs, struct task_req *req)
264 {
265   struct bandwidth_req bw_req;
266   unsigned short slot_id;
267   unsigned char max_try = 3;
268   GList *member_node = NULL;
269   struct rahunas_member *member = NULL;
270
271   member_node = member_get_node_by_id(vs, req->id);
272   if (member_node == NULL)
273     return (-1);
274
275   member = (struct rahunas_member *) member_node->data;
276
277   if (member->bandwidth_max_down == 0 && member->bandwidth_max_up == 0)
278     return 0;
279
280   if (member->bandwidth_slot_id > 0)
281     return 0;
282   
283   // Formating the bandwidth request
284   snprintf(bw_req.ip, sizeof (bw_req.ip), "%s", idtoip(vs->v_map, req->id));
285   snprintf(bw_req.bandwidth_max_down, sizeof (bw_req.bandwidth_max_down), 
286            "%lu", member->bandwidth_max_down);
287   snprintf(bw_req.bandwidth_max_up, sizeof (bw_req.bandwidth_max_up), "%lu", 
288            member->bandwidth_max_up);
289   
290   while (max_try > 0) { 
291     slot_id = _get_slot_id();
292     snprintf(bw_req.slot_id, sizeof (bw_req.slot_id), "%d", slot_id);
293     if (bandwidth_add(vs, &bw_req) == 0)
294       break;
295     else
296       max_try--;
297   }
298
299
300   if (max_try == 0) {
301     logmsg(RH_LOG_ERROR, "[%s] Bandwidth: Maximum trying, failed!",
302            vs->vserver_config->vserver_name);
303     return -1;
304   }
305
306   mark_reserved_slot_id(slot_id);
307   member->bandwidth_slot_id = slot_id;
308  
309   return 0;
310 }
311
312 /* Stop session task */
313 static int stopsess  (struct vserver *vs, struct task_req *req)
314 {
315   struct bandwidth_req bw_req;
316   unsigned short slot_id = 0;
317   GList *member_node = NULL;
318   struct rahunas_member *member = NULL;
319   
320   member_node = member_get_node_by_id(vs, req->id);
321   if (member_node == NULL)
322     return (-1); 
323   
324   member = (struct rahunas_member *) member_node->data;
325   slot_id = member->bandwidth_slot_id;
326
327   if (slot_id < 1)
328     return 0;
329
330   snprintf(bw_req.slot_id, sizeof (bw_req.slot_id), "%d", slot_id);
331
332   if (bandwidth_del(vs, &bw_req) == 0) {
333     member->bandwidth_slot_id = 0;
334
335     if (slot_count > 0)
336       slot_count--;
337   
338     return 0; 
339   } else {
340     return -1;
341   }
342 }
343
344 /* Commit start session task */
345 static int commitstartsess (struct vserver *vs, struct task_req *req)
346 {
347   /* Do nothing or need to implement */
348 }
349
350 /* Commit stop session task */
351 static int commitstopsess  (struct vserver *vs, struct task_req *req)
352 {
353   /* Do nothing or need to implement */
354 }
355
356 /* Rollback start session task */
357 static int rollbackstartsess (struct vserver *vs, struct task_req *req)
358 {
359   /* Do nothing or need to implement */
360 }
361
362 /* Rollback stop session task */
363 static int rollbackstopsess  (struct vserver *vs, struct task_req *req)
364 {
365   /* Do nothing or need to implement */
366 }
367
368 static struct task taskbandwidth = {
369   .taskname = "BANDWIDTH",
370   .taskprio = 20,
371   .init = &init,
372   .cleanup = &cleanup,
373   .startservice = &startservice,
374   .stopservice = &stopservice,
375   .startsess = &startsess,
376   .stopsess = &stopsess,
377   .commitstartsess = &commitstartsess,
378   .commitstopsess = &commitstopsess,
379   .rollbackstartsess = &rollbackstartsess,
380   .rollbackstopsess = &rollbackstopsess,
381 };
382
383 void rh_task_bandwidth_reg(struct main_server *ms) {
384   task_register(ms, &taskbandwidth);
385 }