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