add support for bcapabilities
[util-vserver.git] / lib / planetlab.c
1 /* Copyright 2005 Princeton University
2
3 Redistribution and use in source and binary forms, with or without
4 modification, are permitted provided that the following conditions
5 are met: 
6
7     * Redistributions of source code must retain the above copyright
8       notice, this list of conditions and the following disclaimer.
9       
10     * Redistributions in binary form must reproduce the above
11       copyright notice, this list of conditions and the following
12       disclaimer in the documentation and/or other materials provided
13       with the distribution.
14       
15     * Neither the name of the copyright holder nor the names of its
16       contributors may be used to endorse or promote products derived
17       from this software without specific prior written permission.
18       
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL PRINCETON
23 UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
29 WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 POSSIBILITY OF SUCH DAMAGE. 
31
32 */
33
34 #ifdef HAVE_CONFIG_H
35 #  include <config.h>
36 #endif
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <stdint.h>
42 #include <stdarg.h>
43 #include <unistd.h>
44 #include <ctype.h>
45 #include <sys/resource.h>
46 #include <fcntl.h>
47
48 #include "vserver.h"
49 #include "planetlab.h"
50
51 static int
52 create_context(xid_t ctx, uint64_t bcaps, struct sliver_resources *slr)
53 {
54   struct vc_ctx_caps  vc_caps;
55   struct vc_net_nx  vc_net;
56   struct vc_net_flags  vc_nf;
57
58   /* Create network context */
59   if (vc_net_create(ctx) == VC_NOCTX)
60     return -1;
61
62   /* Make the network context persistent */
63   vc_nf.mask = vc_nf.flagword = VC_NXF_PERSISTENT;
64   if (vc_set_nflags(ctx, &vc_nf))
65     return -1;
66
67   /* XXX: Allow access to all IPv4 addresses (for now) */
68   vc_net.type = vcNET_IPV4;
69   vc_net.count = 1;
70   vc_net.ip[0] = 0;
71   vc_net.mask[0] = 0;
72   if (vc_net_add(ctx, &vc_net) == -1)
73     return -1;
74
75   /*
76    * Create context info - this sets the STATE_SETUP and STATE_INIT flags.
77    */
78   if (vc_ctx_create(ctx) == VC_NOCTX)
79     return -1;
80
81   /* Set capabilities - these don't take effect until SETUP flag is unset */
82   vc_caps.bcaps = bcaps;
83   vc_caps.bmask = ~0ULL;  /* currently unused */
84   vc_caps.ccaps = 0;      /* don't want any of these */
85   vc_caps.cmask = ~0ULL;
86   if (vc_set_ccaps(ctx, &vc_caps))
87     return -1;
88
89   pl_set_limits(ctx, slr);
90
91   return 0;
92 }
93
94 int
95 pl_setup_done(xid_t ctx)
96 {
97   struct vc_ctx_flags  vc_flags;
98
99   /* unset SETUP flag - this allows other processes to migrate */
100   /* set the PERSISTENT flag - so the context doesn't vanish */
101   /* Don't clear the STATE_INIT flag, as that would make us the init task. */
102   vc_flags.mask = VC_VXF_STATE_SETUP|VC_VXF_PERSISTENT;
103   vc_flags.flagword = VC_VXF_PERSISTENT;
104   if (vc_set_cflags(ctx, &vc_flags))
105     return -1;
106
107   return 0;
108 }
109
110 #define RETRY_LIMIT  10
111
112 int
113 pl_chcontext(xid_t ctx, uint64_t bcaps, struct sliver_resources *slr)
114 {
115   int  retry_count = 0;
116   int  net_migrated = 0;
117
118   for (;;)
119     {
120       struct vc_ctx_flags  vc_flags;
121
122       if (vc_get_cflags(ctx, &vc_flags))
123         {
124           if (errno != ESRCH)
125             return -1;
126
127           /* context doesn't exist - create it */
128           if (create_context(ctx, bcaps,slr))
129             {
130               if (errno == EEXIST)
131                 /* another process beat us in a race */
132                 goto migrate;
133               if (errno == EBUSY)
134                 /* another process is creating - poll the SETUP flag */
135                 continue;
136               return -1;
137             }
138
139           /* created context and migrated to it i.e., we're done */
140           return 1;
141         }
142
143       /* check the SETUP flag */
144       if (vc_flags.flagword & VC_VXF_STATE_SETUP)
145         {
146           /* context is still being setup - wait a while then retry */
147           if (retry_count++ >= RETRY_LIMIT)
148             {
149               errno = EBUSY;
150               return -1;
151             }
152           sleep(1);
153           continue;
154         }
155
156       /* context has been setup */
157     migrate:
158       if (net_migrated || !vc_net_migrate(ctx))
159         {
160           if (!vc_ctx_migrate(ctx, 0))
161             break;  /* done */
162           net_migrated = 1;
163         }
164
165       /* context disappeared - retry */
166     }
167
168   return 0;
169 }
170
171 /* it's okay for a syscall to fail because the context doesn't exist */
172 #define VC_SYSCALL(x)                           \
173 do                                              \
174 {                                               \
175   if (x)                                        \
176     return errno == ESRCH ? 0 : -1;             \
177 }                                               \
178 while (0)
179
180 int
181 pl_setsched(xid_t ctx, uint32_t cpu_share, uint32_t cpu_sched_flags)
182 {
183   struct vc_set_sched  vc_sched;
184   struct vc_ctx_flags  vc_flags;
185   uint32_t  new_flags;
186
187   vc_sched.set_mask = (VC_VXSM_FILL_RATE | VC_VXSM_INTERVAL | VC_VXSM_TOKENS |
188                        VC_VXSM_TOKENS_MIN | VC_VXSM_TOKENS_MAX | VC_VXSM_MSEC |
189                        VC_VXSM_FILL_RATE2 | VC_VXSM_INTERVAL2 | VC_VXSM_FORCE |
190                        VC_VXSM_IDLE_TIME);
191   vc_sched.fill_rate = 0;
192   vc_sched.fill_rate2 = cpu_share;  /* tokens accumulated per interval */
193   vc_sched.interval = vc_sched.interval2 = 1000;  /* milliseconds */
194   vc_sched.tokens = 100;     /* initial allocation of tokens */
195   vc_sched.tokens_min = 50;  /* need this many tokens to run */
196   vc_sched.tokens_max = 100;  /* max accumulated number of tokens */
197
198   if (cpu_share == (uint32_t)VC_LIM_KEEP)
199     vc_sched.set_mask &= ~(VC_VXSM_FILL_RATE|VC_VXSM_FILL_RATE2);
200
201   /* guaranteed CPU corresponds to SCHED_SHARE flag being cleared */
202   if (cpu_sched_flags & VS_SCHED_CPU_GUARANTEED) {
203     new_flags = 0;
204     vc_sched.fill_rate = vc_sched.fill_rate2;
205   }
206   else
207     new_flags = VC_VXF_SCHED_SHARE;
208
209   VC_SYSCALL(vc_set_sched(ctx, &vc_sched));
210
211   vc_flags.mask = VC_VXF_SCHED_FLAGS;
212   vc_flags.flagword = new_flags | VC_VXF_SCHED_HARD;
213   VC_SYSCALL(vc_set_cflags(ctx, &vc_flags));
214
215   return 0;
216 }
217
218 struct pl_resources {
219         char *name;
220         unsigned long long *limit;
221 };
222
223 #define WHITESPACE(buffer,index,len)     \
224   while(isspace((int)buffer[index])) \
225         if (index < len) index++; else goto out;
226
227 #define VSERVERCONF "/etc/vservers/"
228 void
229 pl_get_limits(char *context, struct sliver_resources *slr)
230 {
231   FILE *fb;
232   int cwd;
233   size_t len = strlen(VSERVERCONF) + strlen(context) + NULLBYTE_SIZE;
234   char *conf = (char *)malloc(len + strlen("rlimits/openfd.hard"));
235   struct pl_resources *r;
236   struct pl_resources sliver_list[] = {
237     {"sched/fill-rate2", &slr->vs_cpu},
238     {"sched/fill-rate", &slr->vs_cpuguaranteed},
239   
240     {"rlimits/nproc.hard", &slr->vs_nproc.hard},
241     {"rlimits/nproc.soft", &slr->vs_nproc.soft},
242     {"rlimits/nproc.min", &slr->vs_nproc.min},
243   
244     {"rlimits/rss.hard", &slr->vs_rss.hard},
245     {"rlimits/rss.soft", &slr->vs_rss.soft},
246     {"rlimits/rss.min", &slr->vs_rss.min},
247   
248     {"rlimits/as.hard", &slr->vs_as.hard},
249     {"rlimits/as.soft", &slr->vs_as.soft},
250     {"rlimits/as.min", &slr->vs_as.min},
251   
252     {"rlimits/openfd.hard", &slr->vs_openfd.hard},
253     {"rlimits/openfd.soft", &slr->vs_openfd.soft},
254     {"rlimits/openfd.min", &slr->vs_openfd.min},
255
256     {"whitelisted", &slr->vs_whitelisted},
257
258     {"bcapabilities", NULL},
259     {0,0}
260   };
261
262   sprintf(conf, "%s%s", VSERVERCONF, context);
263
264   slr->vs_cpu = VC_LIM_KEEP;
265   slr->vs_cpuguaranteed = 0;
266
267   slr->vs_rss.hard = VC_LIM_KEEP;
268   slr->vs_rss.soft = VC_LIM_KEEP;
269   slr->vs_rss.min = VC_LIM_KEEP;
270
271   slr->vs_as.hard = VC_LIM_KEEP;
272   slr->vs_as.soft = VC_LIM_KEEP;
273   slr->vs_as.min = VC_LIM_KEEP;
274
275
276   slr->vs_nproc.hard = VC_LIM_KEEP;
277   slr->vs_nproc.soft = VC_LIM_KEEP;
278   slr->vs_nproc.min = VC_LIM_KEEP;
279
280   slr->vs_openfd.hard = VC_LIM_KEEP;
281   slr->vs_openfd.soft = VC_LIM_KEEP;
282   slr->vs_openfd.min = VC_LIM_KEEP;
283
284   slr->vs_whitelisted = 1;
285
286   slr->vs_capabilities.bcaps = 0;
287   slr->vs_capabilities.bmask = 0;
288   slr->vs_capabilities.ccaps = 0;
289   slr->vs_capabilities.cmask = 0;
290
291   cwd = open(".", O_RDONLY);
292   if (cwd == -1) {
293     perror("cannot get a handle on .");
294     goto out;
295   }
296   if (chdir(conf) == -1) {
297     fprintf(stderr, "cannot chdir to ");
298     perror(conf);
299     goto out_fd;
300   }
301
302   for (r = &sliver_list[0]; r->name; r++) {
303     char buf[1000];
304     fb = fopen(r->name, "r");
305     if (fb == NULL)
306       continue;
307     /* XXX: UGLY. */
308     if (strcmp(r->name, "bcapabilities") == 0) {
309       size_t len, i;
310       struct vc_err_listparser err;
311
312       len = fread(buf, 1, sizeof(buf), fb);
313       for (i = 0; i < len; i++) {
314         if (buf[i] == '\n')
315           buf[i] = ',';
316       }
317       vc_list2bcap(buf, len, &err, &slr->vs_capabilities);
318     }
319     else
320       if (fgets(buf, sizeof(buf), fb) != NULL && isdigit(*buf))
321         *r->limit = atoi(buf);
322     fclose(fb);
323   }
324
325   fchdir(cwd);
326 out_fd:
327   close(cwd);
328 out:
329   free(conf);
330 }
331
332 int
333 adjust_lim(struct vc_rlimit *vcr, struct rlimit *lim)
334 {
335   int adjusted = 0;
336   if (vcr->min != VC_LIM_KEEP) {
337     if (vcr->min > lim->rlim_cur) {
338       lim->rlim_cur = vcr->min;
339       adjusted = 1;
340     }
341     if (vcr->min > lim->rlim_max) {
342       lim->rlim_max = vcr->min;
343       adjusted = 1;
344     }
345   }
346
347   if (vcr->soft != VC_LIM_KEEP) {
348     switch (vcr->min != VC_LIM_KEEP) {
349     case 1:
350       if (vcr->soft < vcr->min)
351         break;
352     case 0:
353         lim->rlim_cur = vcr->soft;
354         adjusted = 1;
355     }
356   }
357
358   if (vcr->hard != VC_LIM_KEEP) {
359     switch (vcr->min != VC_LIM_KEEP) {
360     case 1:
361       if (vcr->hard < vcr->min)
362         break;
363     case 0:
364         lim->rlim_cur = vcr->hard;
365         adjusted = 1;
366     }
367   }
368   return adjusted;
369 }
370
371 void
372 pl_set_limits(xid_t ctx, struct sliver_resources *slr)
373 {
374   struct rlimit lim; /* getrlimit values */
375   unsigned long long vs_cpu;
376   uint32_t cpu_sched_flags;
377
378   if (slr != 0) {
379     /* set memory limits */
380     getrlimit(RLIMIT_RSS,&lim);
381     if (adjust_lim(&slr->vs_rss, &lim)) {
382       setrlimit(RLIMIT_RSS, &lim);
383       if (vc_set_rlimit(ctx, RLIMIT_RSS, &slr->vs_rss))
384         {
385           PERROR("pl_setrlimit(%u, RLIMIT_RSS)", ctx);
386           exit(1);
387         }
388     }
389
390     /* set address space limits */
391     getrlimit(RLIMIT_AS,&lim);
392     if (adjust_lim(&slr->vs_as, &lim)) {
393       setrlimit(RLIMIT_AS, &lim);
394       if (vc_set_rlimit(ctx, RLIMIT_AS, &slr->vs_as))
395         {
396           PERROR("pl_setrlimit(%u, RLIMIT_AS)", ctx);
397           exit(1);
398         }
399     }
400     /* set nrpoc limit */
401     getrlimit(RLIMIT_NPROC,&lim);
402     if (adjust_lim(&slr->vs_nproc, &lim)) {
403       setrlimit(RLIMIT_NPROC, &lim);
404       if (vc_set_rlimit(ctx, RLIMIT_NPROC, &slr->vs_nproc))
405         {
406           PERROR("pl_setrlimit(%u, RLIMIT_NPROC)", ctx);
407           exit(1);
408         }
409     }
410
411     /* set openfd limit */
412     getrlimit(RLIMIT_NOFILE,&lim);
413     if (adjust_lim(&slr->vs_openfd, &lim)) {
414       setrlimit(RLIMIT_NOFILE, &lim);
415       if (vc_set_rlimit(ctx, RLIMIT_NOFILE, &slr->vs_openfd))
416         {
417           PERROR("pl_setrlimit(%u, RLIMIT_NOFILE)", ctx);
418           exit(1);
419         }
420       if (vc_set_rlimit(ctx, VC_VLIMIT_OPENFD, &slr->vs_openfd))
421         {
422           PERROR("pl_setrlimit(%u, VLIMIT_OPENFD)", ctx);
423           exit(1);
424         }
425     }
426     vs_cpu = slr->vs_cpu;    
427     cpu_sched_flags = slr->vs_cpuguaranteed & VS_SCHED_CPU_GUARANTEED;
428
429     slr->vs_capabilities.bmask = vc_get_insecurebcaps();
430     if (vc_set_ccaps(ctx, &slr->vs_capabilities) < 0) {
431       PERROR("pl_setcaps(%u)", ctx);
432       exit(1);
433     }
434   } else {
435     vs_cpu = 1;
436     cpu_sched_flags = 0;
437   }
438   
439   if (pl_setsched(ctx, vs_cpu, cpu_sched_flags) < 0) {
440     PERROR("pl_setsched(%u)", ctx);
441     exit(1);
442   }
443 }