clean up to produce less warnings
[util-vserver-pl.git] / src / 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 <sys/types.h>
47 #include <fcntl.h>
48 #define _GNU_SOURCE 
49 #include <sched.h>
50
51 #include "vserver.h"
52 #include "planetlab.h"
53
54 /* defined in netns.c */
55 extern uint32_t get_space_flag(xid_t);
56
57 #ifndef VC_NXC_RAW_SOCKET
58 #  define VC_NXC_RAW_SOCKET     0x00000200ull
59 #endif
60 #ifndef VC_NXC_RAW_SEND
61 #  define VC_NXC_RAW_SEND       0x00000400ull
62 #endif
63 #ifndef VC_NXF_LBACK_ALLOW
64 #  define VC_NXF_LBACK_ALLOW    0x00000400ull
65 #endif
66
67 static int
68 create_context(xid_t ctx, uint64_t bcaps, uint32_t unshare_flags)
69 {
70   struct vc_ctx_caps   vc_caps;
71   struct vc_net_flags  vc_nf;
72   struct vc_net_caps   vc_ncaps;
73
74   /* Create network context */
75   if (vc_net_create(ctx) == VC_NOCTX) {
76     if (errno == EEXIST)
77       goto tag;
78     return -1;
79   }
80
81   /* Make the network context persistent */
82   vc_nf.mask = vc_nf.flagword = VC_NXF_PERSISTENT | VC_NXF_LBACK_ALLOW;
83   if (vc_set_nflags(ctx, &vc_nf))
84     return -1;
85
86   /* Give it raw sockets capabilities */
87   vc_ncaps.ncaps = vc_ncaps.cmask = VC_NXC_RAW_ICMP | VC_NXC_RAW_SOCKET;
88   if (vc_set_ncaps(ctx, &vc_ncaps))
89     return -1;
90
91 tag:
92   /* Create tag context */
93   if (vc_tag_create(ctx) == VC_NOCTX)
94     return -1;
95
96   /*
97    * Create context info - this sets the STATE_SETUP and STATE_INIT flags.
98    */
99   if (vc_ctx_create(ctx, 0) == VC_NOCTX)
100     return -1;
101
102     if (unshare_flags != 0) {
103       unshare(unshare_flags);
104       unshare_flags |= vc_get_space_mask();
105       vc_set_namespace(ctx, unshare_flags);
106   }
107
108   /* Set capabilities - these don't take effect until SETUP flag is unset */
109   vc_caps.bcaps = bcaps;
110   vc_caps.bmask = ~0ULL;  /* currently unused */
111   vc_caps.ccaps = 0;      /* don't want any of these */
112   vc_caps.cmask = ~0ULL;
113   if (vc_set_ccaps(ctx, &vc_caps))
114     return -1;
115
116   if (pl_setsched(ctx, 0, 1) < 0) {
117     PERROR("pl_setsched(%u)", ctx);
118     exit(1);
119   }
120
121   return 0;
122 }
123
124 int
125 pl_setup_done(xid_t ctx)
126 {
127   struct vc_ctx_flags  vc_flags;
128
129   /* unset SETUP flag - this allows other processes to migrate */
130   /* set the PERSISTENT flag - so the context doesn't vanish */
131   /* Don't clear the STATE_INIT flag, as that would make us the init task. */
132   vc_flags.mask = VC_VXF_STATE_SETUP|VC_VXF_PERSISTENT;
133   vc_flags.flagword = VC_VXF_PERSISTENT;
134   if (vc_set_cflags(ctx, &vc_flags))
135     return -1;
136
137   return 0;
138 }
139
140 #define RETRY_LIMIT  10
141
142 int
143 pl_chcontext(xid_t ctx, uint64_t bcaps, const struct sliver_resources *slr)
144 {
145   int  retry_count = 0;
146   int  net_migrated = 0;
147
148   if (pl_set_ulimits(slr) != 0)
149     return -1;
150
151   for (;;)
152     {
153       struct vc_ctx_flags  vc_flags;
154
155       if (vc_get_cflags(ctx, &vc_flags))
156         {
157         uint32_t unshare_flags;
158           if (errno != ESRCH)
159             return -1;
160
161        /* Unshare the net namespace if the slice if requested in the local slice configuration */
162         unshare_flags = get_space_flag(ctx);
163
164           /* context doesn't exist - create it */
165           if (create_context(ctx, bcaps, unshare_flags))
166             {
167               if (errno == EEXIST)
168                 /* another process beat us in a race */
169                 goto migrate;
170               if (errno == EBUSY)
171                 /* another process is creating - poll the SETUP flag */
172                 continue;
173               return -1;
174             }
175
176           /* created context and migrated to it i.e., we're done */
177           return 1;
178         }
179
180       /* check the SETUP flag */
181       if (vc_flags.flagword & VC_VXF_STATE_SETUP)
182         {
183           /* context is still being setup - wait a while then retry */
184           if (retry_count++ >= RETRY_LIMIT)
185             {
186               errno = EBUSY;
187               return -1;
188             }
189           sleep(1);
190           continue;
191         }
192
193       /* context has been setup */
194     migrate:
195       if (net_migrated || !vc_net_migrate(ctx))
196         {
197         uint32_t unshare_flags;
198       /* Unshare the net namespace if the slice if requested in the local slice configuration */
199       unshare_flags = get_space_flag(ctx);
200       if (unshare_flags != 0) {
201           unshare_flags |=vc_get_space_mask();
202           vc_enter_namespace(ctx, unshare_flags);
203       }
204
205           if (!vc_tag_migrate(ctx) && !vc_ctx_migrate(ctx, 0))
206             break;  /* done */
207           net_migrated = 1;
208         }
209
210       /* context disappeared - retry */
211     }
212
213   return 0;
214 }
215
216 /* it's okay for a syscall to fail because the context doesn't exist */
217 #define VC_SYSCALL(x)                           \
218 do                                              \
219 {                                               \
220   if (x)                                        \
221     return errno == ESRCH ? 0 : -1;             \
222 }                                               \
223 while (0)
224
225 int
226 pl_setsched(xid_t ctx, uint32_t cpu_min, uint32_t cpu_share)
227 {
228   struct vc_set_sched  vc_sched;
229   struct vc_ctx_flags  vc_flags;
230
231   vc_sched.set_mask = (VC_VXSM_FILL_RATE | VC_VXSM_INTERVAL | VC_VXSM_TOKENS |
232                        VC_VXSM_TOKENS_MIN | VC_VXSM_TOKENS_MAX | VC_VXSM_MSEC |
233                        VC_VXSM_FILL_RATE2 | VC_VXSM_INTERVAL2 | VC_VXSM_FORCE);
234   vc_sched.fill_rate = cpu_min; /* percent reserved */
235   vc_sched.interval = 100;
236   vc_sched.fill_rate2 = cpu_share; /* best-effort fair share of unreserved */
237   vc_sched.interval2 = 1000;  /* milliseconds */
238   vc_sched.tokens = 100;     /* initial allocation of tokens */
239   vc_sched.tokens_min = 50;  /* need this many tokens to run */
240   vc_sched.tokens_max = 100;  /* max accumulated number of tokens */
241
242   if (cpu_share) {
243     if (cpu_share == (uint32_t)VC_LIM_KEEP)
244       vc_sched.set_mask &= ~(VC_VXSM_FILL_RATE|VC_VXSM_FILL_RATE2);
245     else
246       vc_sched.set_mask |= VC_VXSM_IDLE_TIME;
247   }
248
249   VC_SYSCALL(vc_set_sched(ctx, &vc_sched));
250
251   vc_flags.mask = VC_VXF_SCHED_FLAGS;
252   vc_flags.flagword = VC_VXF_SCHED_HARD;
253   VC_SYSCALL(vc_set_cflags(ctx, &vc_flags));
254
255   return 0;
256 }
257
258 enum {
259   TYPE_LONG = 1,
260   TYPE_PERS = 2,
261 };
262
263 struct pl_resources {
264         char *name;
265         unsigned type;
266   union {
267     unsigned long long *limit;
268     unsigned long int *personality;
269   };
270 };
271
272 #define WHITESPACE(buffer,index,len)     \
273   while(isspace((int)buffer[index])) \
274         if (index < len) index++; else goto out;
275
276 #define VSERVERCONF "/etc/vservers/"
277
278 void
279 pl_get_limits(const char *context, struct sliver_resources *slr)
280 {
281   FILE *fb;
282   int cwd;
283   size_t len = strlen(VSERVERCONF) + strlen(context) + NULLBYTE_SIZE;
284   char *conf = (char *)malloc(len + strlen("rlimits/openfd.hard"));
285   struct pl_resources *r;
286   struct pl_resources sliver_list[] = {
287     {"sched/fill-rate2", TYPE_LONG, &slr->vs_cpu},
288
289     {"rlimits/nproc.hard", TYPE_LONG, &slr->vs_nproc.hard},
290     {"rlimits/nproc.soft", TYPE_LONG, &slr->vs_nproc.soft},
291     {"rlimits/nproc.min", TYPE_LONG, &slr->vs_nproc.min},
292   
293     {"rlimits/rss.hard", TYPE_LONG, &slr->vs_rss.hard},
294     {"rlimits/rss.soft", TYPE_LONG, &slr->vs_rss.soft},
295     {"rlimits/rss.min", TYPE_LONG, &slr->vs_rss.min},
296   
297     {"rlimits/as.hard", TYPE_LONG, &slr->vs_as.hard},
298     {"rlimits/as.soft", TYPE_LONG, &slr->vs_as.soft},
299     {"rlimits/as.min", TYPE_LONG, &slr->vs_as.min},
300   
301     {"rlimits/openfd.hard", TYPE_LONG, &slr->vs_openfd.hard},
302     {"rlimits/openfd.soft", TYPE_LONG, &slr->vs_openfd.soft},
303     {"rlimits/openfd.min", TYPE_LONG, &slr->vs_openfd.min},
304
305     {"rlimits/memlock.hard", TYPE_LONG, &slr->vs_memlock.hard},
306     {"rlimits/memlock.soft", TYPE_LONG, &slr->vs_memlock.soft},
307     {"rlimits/memlock.min", TYPE_LONG, &slr->vs_memlock.min},
308
309     {"personality", TYPE_PERS, &slr->personality},
310
311     {0,0}
312   };
313
314   sprintf(conf, "%s%s", VSERVERCONF, context);
315
316   slr->vs_rss.hard = VC_LIM_KEEP;
317   slr->vs_rss.soft = VC_LIM_KEEP;
318   slr->vs_rss.min = VC_LIM_KEEP;
319
320   slr->vs_as.hard = VC_LIM_KEEP;
321   slr->vs_as.soft = VC_LIM_KEEP;
322   slr->vs_as.min = VC_LIM_KEEP;
323
324   slr->vs_nproc.hard = VC_LIM_KEEP;
325   slr->vs_nproc.soft = VC_LIM_KEEP;
326   slr->vs_nproc.min = VC_LIM_KEEP;
327
328   slr->vs_openfd.hard = VC_LIM_KEEP;
329   slr->vs_openfd.soft = VC_LIM_KEEP;
330   slr->vs_openfd.min = VC_LIM_KEEP;
331
332   slr->vs_memlock.hard = VC_LIM_KEEP;
333   slr->vs_memlock.soft = VC_LIM_KEEP;
334   slr->vs_memlock.min = VC_LIM_KEEP;
335
336   slr->personality = 0;
337
338   cwd = open(".", O_RDONLY);
339   if (cwd == -1) {
340     perror("cannot get a handle on .");
341     goto out;
342   }
343   if (chdir(conf) == -1) {
344     fprintf(stderr, "cannot chdir to ");
345     perror(conf);
346     goto out_fd;
347   }
348
349   for (r = &sliver_list[0]; r->name; r++) {
350     char buf[1000];
351     fb = fopen(r->name, "r");
352     if (fb == NULL)
353       continue;
354     if (fgets(buf, sizeof(buf), fb) != NULL) {
355       len=strlen(buf);
356       /* remove trailing newline */
357       if (buf[len-1] == '\n') {
358         buf[len-1]='\0';
359         len --;
360       }
361       if (r->type == TYPE_LONG) {
362         int val;
363         char *res=0;
364         errno=0;
365         val = strtol(buf,&res,0);
366         if ( !( (val==0 && res) || (errno!=0) ) )
367           *r->limit = val;
368       } else if ( (r->type == TYPE_PERS) && isalpha(*buf)) {
369         unsigned long int res;
370         res = vc_str2personalitytype(buf,len);
371         if (res != VC_BAD_PERSONALITY) {
372           *r->personality = res;
373         }
374       }
375     }
376     
377     fclose(fb);
378   }
379
380   (void)fchdir(cwd);
381 out_fd:
382   close(cwd);
383 out:
384   free(conf);
385 }
386
387 int
388 adjust_lim(const struct vc_rlimit *vcr, struct rlimit *lim)
389 {
390   int adjusted = 0;
391   if (vcr->min != VC_LIM_KEEP) {
392     if (vcr->min > lim->rlim_cur) {
393       lim->rlim_cur = vcr->min;
394       adjusted = 1;
395     }
396     if (vcr->min > lim->rlim_max) {
397       lim->rlim_max = vcr->min;
398       adjusted = 1;
399     }
400   }
401
402   if (vcr->soft != VC_LIM_KEEP) {
403     switch (vcr->min != VC_LIM_KEEP) {
404     case 1:
405       if (vcr->soft < vcr->min)
406         break;
407     case 0:
408         lim->rlim_cur = vcr->soft;
409         adjusted = 1;
410     }
411   }
412
413   if (vcr->hard != VC_LIM_KEEP) {
414     switch (vcr->min != VC_LIM_KEEP) {
415     case 1:
416       if (vcr->hard < vcr->min)
417         break;
418     case 0:
419         lim->rlim_max = vcr->hard;
420         adjusted = 1;
421     }
422   }
423   return adjusted;
424 }
425
426 static inline void
427 set_one_ulimit(int resource, const struct vc_rlimit *limit)
428 {
429   struct rlimit lim;
430   getrlimit(resource, &lim);
431   adjust_lim(limit, &lim);
432   setrlimit(resource, &lim);
433 }
434
435 static inline int 
436 set_personality(unsigned long int personality_arg)
437 {
438   if (personality_arg == 0) 
439     return 0;
440   if (personality(personality_arg) < 0) {
441     return -1;
442   }
443   return 0;
444 }
445
446 int
447 pl_set_ulimits(const struct sliver_resources *slr)
448 {
449   if (!slr)
450     return 0;
451
452   set_one_ulimit(RLIMIT_RSS, &slr->vs_rss);
453   set_one_ulimit(RLIMIT_AS, &slr->vs_as);
454   set_one_ulimit(RLIMIT_NPROC, &slr->vs_nproc);
455   set_one_ulimit(RLIMIT_NOFILE, &slr->vs_openfd);
456   set_one_ulimit(RLIMIT_MEMLOCK, &slr->vs_memlock);
457   return set_personality(slr->personality);
458 }