Rename flags to cflags.
[util-vserver.git] / src / vlimit.c
1 // $Id$
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2, or (at your option)
8 // any later version.
9 //  
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //  
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19 /*
20         Set the global per context limit of a resource (memory, file handle).
21         This utility can do it either for the current context or a selected
22         one.
23
24         It uses the same options as ulimit, when possible
25 */
26 #ifdef HAVE_CONFIG_H
27 #  include <config.h>
28 #endif
29 #include "compat.h"
30
31 #include "vserver.h"
32 #include "internal.h"
33 #include "util.h"
34
35 #include <getopt.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <assert.h>
39 #include <stdbool.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <libgen.h>
43 #include <sys/resource.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <sys/stat.h>
47 #include <ctype.h>
48
49 #define ENSC_WRAPPERS_PREFIX    "vlimit: "
50 #define ENSC_WRAPPERS_UNISTD    1
51 #define ENSC_WRAPPERS_VSERVER   1
52 #include <wrappers.h>
53
54 #define CMD_HELP                0x1000
55 #define CMD_VERSION             0x1001
56 #define CMD_XID                 0x4000
57 #define CMD_DIR                 0x8000
58 #define CMD_MISSINGOK           0x8001
59
60 int             wrapper_exit_code = 255;
61
62 #ifndef RLIMIT_MSGQUEUE
63 #  define RLIMIT_MSGQUEUE       12
64 #endif
65
66 #define NUMLIM(X) \
67 { #X, required_argument, 0, 2048|X }
68 #define OPT_RESLIM(RES,V) \
69   { #RES, required_argument, 0, 2048|RLIMIT_##V }
70 #define OPT_VLIMIT(RES,V) \
71   { #RES, required_argument, 0, 2048|VC_VLIMIT_##V }
72
73 static struct option const
74 CMDLINE_OPTIONS[] = {
75   { "help",      no_argument,       0, CMD_HELP },
76   { "version",   no_argument,       0, CMD_VERSION },
77   { "all",       no_argument,       0, 'a' },
78   { "xid",       required_argument, 0, CMD_XID },
79   { "dir",       required_argument, 0, CMD_DIR },
80   { "missingok", no_argument,       0, CMD_MISSINGOK },
81   NUMLIM( 0), NUMLIM( 1), NUMLIM( 2), NUMLIM( 3),
82   NUMLIM( 4), NUMLIM( 5), NUMLIM( 6), NUMLIM( 7),
83   NUMLIM( 8), NUMLIM( 9), NUMLIM(10), NUMLIM(11),
84   NUMLIM(12), NUMLIM(13), NUMLIM(14), NUMLIM(15),
85   NUMLIM(16), NUMLIM(17), NUMLIM(18), NUMLIM(19),
86   NUMLIM(20), NUMLIM(21), NUMLIM(22), NUMLIM(23),
87   NUMLIM(24), NUMLIM(25), NUMLIM(26), NUMLIM(27),
88   NUMLIM(28), NUMLIM(29), NUMLIM(30), NUMLIM(31),
89   OPT_RESLIM(cpu,      CPU),
90   OPT_RESLIM(fsize,    FSIZE),
91   OPT_RESLIM(data,     DATA),
92   OPT_RESLIM(stack,    STACK),
93   OPT_RESLIM(core,     CORE),
94   OPT_RESLIM(rss,      RSS),
95   OPT_RESLIM(nproc,    NPROC),
96   OPT_RESLIM(nofile,   NOFILE),
97   OPT_RESLIM(memlock,  MEMLOCK),
98   OPT_RESLIM(as,       AS),
99   OPT_RESLIM(locks,    LOCKS),
100   OPT_RESLIM(msgqueue, MSGQUEUE),
101   OPT_VLIMIT(nsock,    NSOCK),
102   OPT_VLIMIT(openfd,   OPENFD),
103   OPT_VLIMIT(anon,     ANON),
104   OPT_VLIMIT(shmem,    SHMEM),
105   OPT_VLIMIT(semary,   SEMARY),
106   OPT_VLIMIT(nsems,    NSEMS),
107   OPT_VLIMIT(dentry,   DENTRY),
108   { 0,0,0,0 }
109 };
110
111 #define REV_RESLIM(X)   [RLIMIT_##X] = #X
112 #define REV_VLIMIT(X)   [VC_VLIMIT_##X] = #X
113 static char const * const LIMIT_STR[] = {
114   REV_RESLIM(CPU),     REV_RESLIM(FSIZE),  REV_RESLIM(DATA),  REV_RESLIM(STACK),
115   REV_RESLIM(CORE),    REV_RESLIM(RSS),    REV_RESLIM(NPROC), REV_RESLIM(NOFILE),
116   REV_RESLIM(MEMLOCK), REV_RESLIM(AS),     REV_RESLIM(LOCKS), REV_RESLIM(MSGQUEUE),
117   REV_VLIMIT(NSOCK),   REV_VLIMIT(OPENFD), REV_VLIMIT(ANON),  REV_VLIMIT(SHMEM),
118   REV_VLIMIT(SEMARY),  REV_VLIMIT(NSEMS),  REV_VLIMIT(DENTRY),
119 };
120
121 static void
122 showHelp(int fd, char const *cmd, int res)
123 {
124   VSERVER_DECLARE_CMD(cmd);
125   
126   WRITE_MSG(fd, "Usage:  ");
127   WRITE_STR(fd, cmd);
128   WRITE_MSG(fd,
129             " [--xid|-c <xid>] [-nd] [-a|--all] [[-MSH] --(<resource>|<nr>) <value>]*\n"
130             "               [--dir <pathname> [--missingok]] [--] [<program> <args>*]\n\n"
131             "Options:\n"
132             "    -c|--xid <xid>\n"
133             "                ...  operate on context <xid>\n"
134             "    -a|--all    ...  show all available limits\n"
135             "    -n          ...  do not resolve limit-names\n"
136             "    -d          ...  show limits in decimal\n"
137             "    -M          ...  set Minimum limit\n"
138             "    -S          ...  set Soft limit\n"
139             "    -H          ...  set Hard limit (assumed by default, when neither\n"
140             "                     M nor S was requested)\n"
141             "    --dir <pathname>\n"
142             "                ...  read limits from <pathname>/; allowed filenames are\n"
143             "                     <resource> and <resource>.{min,soft,hard}. When a limit\n"
144             "                     was set by the CLI already, the corresponding file\n"
145             "                     will be ignored\n"
146             "    --missingok ...  do not fail when <pathname> does not exist\n"
147             "    --<resource>|<nr> <value>\n"
148             "                ...  set specified (MSH) limit for <resource> to <value>\n\n"
149             "Valid values for resource are cpu, fsize, data, stack, core, rss, nproc,\n"
150             "nofile, memlock, as, locks, msgqueue, nsock, openfd, anon, shmem, semary,\n"
151             "nsems, and dentry.\n\n"
152             "Please report bugs to " PACKAGE_BUGREPORT "\n");
153   exit(res);
154 }
155
156 static void
157 showVersion()
158 {
159   WRITE_MSG(1,
160             "vlimit " VERSION " -- limits context-resources\n"
161             "This program is part of " PACKAGE_STRING "\n\n"
162             "Copyright (C) 2003 Enrico Scholz\n"
163             VERSION_COPYRIGHT_DISCLAIMER);
164   exit(0);
165 }
166
167 static size_t
168 fmtHex(char *ptr, vc_limit_t lim)
169 {
170   memcpy(ptr, "0x", 2);
171   return utilvserver_fmt_xuint64(ptr+2, lim) + 2;
172 }
173
174 static bool             do_resolve = true;
175 static size_t           (*fmt_func)(char *, vc_limit_t) = fmtHex;
176
177 static void *
178 appendLimit(char *ptr, bool do_it, vc_limit_t lim)
179 {
180   memcpy(ptr, "  ", 2);
181   ptr += 2;
182   if (do_it) {
183     if (lim==VC_LIM_INFINITY) {
184       memcpy(ptr, "inf", 3);
185       ptr += 3;
186     }
187     else {
188       ptr += (*fmt_func)(ptr, lim);
189       *ptr = ' ';
190     }
191   }
192   else {
193     memcpy(ptr, "N/A", 3);
194     ptr += 3;
195   }
196
197   return ptr;
198 }
199
200 static void
201 showAll(int ctx)
202 {
203   struct vc_rlimit_mask mask;
204   size_t                i;
205
206   if (vc_get_rlimit_mask(ctx, &mask)==-1) {
207     perror("vc_get_rlimit_mask()");
208     exit(wrapper_exit_code);
209   }
210
211   for (i=0; i<32; ++i) {
212     uint32_t            bitmask = (1<<i);
213     struct vc_rlimit    limit;
214     char                buf[128], *ptr=buf;
215
216     if (((mask.min|mask.soft|mask.hard) & bitmask)==0) continue;
217     if (vc_get_rlimit(ctx, i, &limit)==-1) {
218       perror("vc_get_rlimit()");
219       continue;
220     }
221
222     memset(buf, ' ', sizeof buf);
223     if (do_resolve && i<DIM_OF(LIMIT_STR)) {
224       size_t            l = strlen(LIMIT_STR[i]);
225       memcpy(ptr, LIMIT_STR[i], l);
226       ptr += l;
227     }
228     else {
229       ptr += utilvserver_fmt_uint(ptr, i);
230       *ptr = ' ';
231     }
232
233     ptr  = appendLimit(buf+10, mask.min &bitmask, limit.min);
234     ptr  = appendLimit(buf+30, mask.soft&bitmask, limit.soft);
235     ptr  = appendLimit(buf+50, mask.hard&bitmask, limit.hard);
236
237     *ptr++ = '\n';
238     Vwrite(1, buf, ptr-buf);
239   }
240 }
241
242 static void
243 setLimits(int ctx, struct vc_rlimit const limits[], uint32_t mask)
244 {
245   size_t                i;
246   for (i=0; i<32; ++i) {
247     if ((mask & (1<<i))==0) continue;
248     if (vc_set_rlimit(ctx, i, limits+i)) {
249       perror("vc_set_rlimit()");
250     }
251   }
252 }
253
254 static vc_limit_t
255 readValue(int fd, char const *filename)
256 {
257   char          buf[128];
258   size_t        len = Eread(fd, buf, sizeof(buf)-1);
259   vc_limit_t    res;
260
261   buf[len] = '\0';
262
263   if (!vc_parseLimit(buf, &res)) {
264     WRITE_MSG(2, "Invalid limit in '");
265     WRITE_STR(2, filename);
266     WRITE_STR(2, "'\n");
267     exit(wrapper_exit_code);
268   }
269
270   return res;
271 }
272
273 static bool
274 readFile(char const *file, char *base, char const *suffix,
275          vc_limit_t *limit)
276 {
277   int           fd;
278   
279   strcpy(base, suffix);
280   fd = open(file, O_RDONLY);
281   if (fd!=-1) {
282     *limit = readValue(fd, file);
283     Eclose(fd);
284   }
285
286   return fd!=-1;
287 }
288          
289 static void
290 readFromDir(struct vc_rlimit limits[32], uint_least32_t *mask,
291             char const *pathname, bool missing_ok)
292 {
293   struct stat           st;
294   size_t                i;
295   size_t                l_pathname = strlen(pathname);
296   char                  buf[l_pathname + sizeof("/memlock.hard") + 32];
297   
298   if (stat(pathname, &st)==-1) {
299     if (errno==ENOENT && missing_ok) return;
300     PERROR_Q("vlimit: fstat", pathname);
301     exit(wrapper_exit_code);
302   }
303
304   memcpy(buf, pathname, l_pathname);
305   if (l_pathname>0 && pathname[l_pathname-1]!='/')
306     buf[l_pathname++] = '/';
307     
308   for (i=0; i<DIM_OF(LIMIT_STR); ++i) {
309     size_t      l_res;
310     char *      ptr   = buf+l_pathname;
311
312     // ignore unimplemented limits
313     if (LIMIT_STR[i]==0) continue;
314     
315     // ignore limits set on cli already
316     if (*mask & (1<<i)) continue;
317
318     l_res = strlen(LIMIT_STR[i]);
319     memcpy(ptr, LIMIT_STR[i], l_res+1);
320     while (*ptr) {
321       *ptr = tolower(*ptr);
322       ++ptr;
323     }
324
325     if (readFile(buf, ptr, "", &limits[i].min)) {
326       limits[i].soft = limits[i].hard = limits[i].min;
327       *mask |= (1<<i);
328     }
329
330     if (readFile(buf, ptr, ".min",  &limits[i].min))
331       *mask |= (1<<i);
332
333     if (readFile(buf, ptr, ".soft", &limits[i].soft))
334       *mask |= (1<<i);
335
336     if (readFile(buf, ptr, ".hard", &limits[i].hard))
337       *mask |= (1<<i);
338   }
339 }
340
341 int main (int argc, char *argv[])
342 {
343   // overall used limits
344   uint32_t              lim_mask = 0;
345   int                   set_mask = 0;
346   struct vc_rlimit      limits[32];
347   bool                  show_all   = false;
348   xid_t                 ctx        = VC_NOCTX;
349   char const *          dir        = 0;
350   bool                  missing_ok = false;
351
352   {
353     size_t              i;
354     for (i=0; i<32; ++i) {
355       limits[i].min  = VC_LIM_KEEP;
356       limits[i].soft = VC_LIM_KEEP;
357       limits[i].hard = VC_LIM_KEEP;
358     }
359   }
360   
361   while (1) {
362     int         c = getopt_long(argc, argv, "+MSHndac:", CMDLINE_OPTIONS, 0);
363     if (c==-1) break;
364
365     if (2048<=c && c<2048+32) {
366       int               id  = c-2048;
367       vc_limit_t        val;
368       
369       if (!vc_parseLimit(optarg, &val)) {
370         WRITE_MSG(2, "Can not parse limit '");
371         WRITE_STR(2, optarg);
372         WRITE_STR(2, "'\n");
373         exit(wrapper_exit_code);
374       }
375
376       if (set_mask==0)  set_mask=4;
377       
378       if (set_mask & 1) limits[id].min  = val;
379       if (set_mask & 2) limits[id].soft = val;
380       if (set_mask & 4) limits[id].hard = val;
381
382       lim_mask |= (1<<id);
383       set_mask  = 0;
384     }
385     else switch (c) {
386       case CMD_HELP     :  showHelp(1, argv[0], 0);
387       case CMD_VERSION  :  showVersion();
388       case 'a'          :  show_all   = true;            break;
389       case 'n'          :  do_resolve = false;           break;
390       case CMD_DIR      :  dir        = optarg;          break;
391       case CMD_MISSINGOK:  missing_ok = true;            break;
392       case CMD_XID      :  /*@fallthrough@*/
393       case 'c'          :  ctx = Evc_xidopt2xid(optarg,true);   break;
394       case 'd'          :  fmt_func   = utilvserver_fmt_uint64; break;
395       case 'M'          :
396       case 'S'          :
397       case 'H'          :
398         switch (c) {
399           case 'M'      :  set_mask |= 1; break;
400           case 'S'      :  set_mask |= 2; break;
401           case 'H'      :  set_mask |= 4; break;
402           default       :  assert(false);
403         }
404         break;
405         
406       default           :
407         WRITE_MSG(2, "Try '");
408         WRITE_STR(2, argv[0]);
409         WRITE_MSG(2, " --help' for more information.\n");
410         exit(wrapper_exit_code) ;
411         break;
412     }
413   }
414
415   if (ctx==VC_NOCTX)
416     ctx = Evc_get_task_xid(0);
417
418   if (dir)
419     readFromDir(limits, &lim_mask, dir, missing_ok);
420
421   setLimits(ctx, limits, lim_mask);
422   if (show_all) showAll(ctx);
423
424   if (optind<argc)
425     EexecvpD(argv[optind], argv+optind);
426
427   return EXIT_SUCCESS;
428 }