db8bd8804a8ffc42f5e415f7778592535f84bad3
[util-vserver.git] / src / vdlimit.c
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2005 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; version 2 of the License.
8 //  
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //  
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include "util.h"
24 #include <lib_internal/sys_clone.h>
25 #include <lib/internal.h>
26
27 #include <vserver.h>
28
29 #include <getopt.h>
30 #include <libgen.h>
31 #include <errno.h>
32 #include <signal.h>
33 #include <sched.h>
34
35 #define ENSC_WRAPPERS_PREFIX    "vdlimit: "
36 #define ENSC_WRAPPERS_UNISTD    1
37 #define ENSC_WRAPPERS_VSERVER   1
38 #include <wrappers.h>
39
40 #define CMD_HELP                0x1000
41 #define CMD_VERSION             0x1001
42
43 int             wrapper_exit_code  =  1;
44
45 struct option const
46 CMDLINE_OPTIONS[] = {
47   { "help",       no_argument,       0, CMD_HELP },
48   { "version",    no_argument,       0, CMD_VERSION },
49   { "xid",        required_argument, 0, 'x' },
50   { "set",        required_argument, 0, 's' },
51   { "remove",     no_argument,       0, 'd' },
52   { "flags",      required_argument, 0, 'f' },
53   {0,0,0,0}
54 };
55
56 static void
57 showHelp(int fd, char const *cmd)
58 {
59   WRITE_MSG(fd, "Usage: ");
60   WRITE_STR(fd, cmd);
61   WRITE_MSG(fd,
62             " --xid <xid> [--flags <flags>] (--set <limit>=<value>|--remove) <mount point>\n"
63             "\n"
64             "    --set|-s <limit>=<value>  ...  set <limit> to <value>, where limit is \n"
65             "                           one of: space_used, space_total, inodes_used,\n"
66             "                           inodes_total, reserved\n"
67             "    --remove|-d       ...  removes the disk limit for <xid> from <mount point>\n"
68             "\n"
69             "Please report bugs to " PACKAGE_BUGREPORT "\n");
70
71   exit(0);
72 }
73
74 static void
75 showVersion()
76 {
77   WRITE_MSG(1,
78             "vdlimit " VERSION " -- manages disk limits\n"
79             "This program is part of " PACKAGE_STRING "\n\n"
80             "Copyright (C) 2005 Enrico Scholz\n"
81             VERSION_COPYRIGHT_DISCLAIMER);
82   exit(0);
83 }
84
85 static void
86 setDlimit(char const *filename, xid_t xid, uint32_t flags, struct vc_ctx_dlimit const *limit)
87 {
88   bool          was_added = false;
89
90   if (vc_get_dlimit(filename, xid, flags, 0) == -1) {
91     if (vc_add_dlimit(filename, xid, flags) == -1) {
92       perror(ENSC_WRAPPERS_PREFIX "vc_add_dlimit()");
93       exit(wrapper_exit_code);
94     }
95
96     was_added = true;
97   }
98
99   if (vc_set_dlimit(filename, xid, flags, limit) == -1) {
100     perror(ENSC_WRAPPERS_PREFIX "vc_set_dlimit()");
101
102     if (was_added &&
103         vc_rem_dlimit(filename, xid, flags)==-1)
104       perror(ENSC_WRAPPERS_PREFIX "vc_rem_dlimit()");
105
106     exit(wrapper_exit_code);
107   }
108 }
109
110 static void
111 remDlimit(char const *filename, xid_t xid, uint32_t flags)
112 {
113   if (vc_rem_dlimit(filename, xid, flags) == -1) {
114     perror(ENSC_WRAPPERS_PREFIX "vc_rem_dlimit()");
115     exit(wrapper_exit_code);
116   }
117 }
118
119 static void
120 writeInt(int fd, char const *prefix, int val)
121 {
122   char          buf[sizeof(val)*3 + 2];
123   size_t        len = utilvserver_fmt_int(buf, val);
124
125   if (prefix)
126     WRITE_STR(fd, prefix);
127   Vwrite(fd, buf, len);
128 }
129
130 static void
131 printDlimit(char const *filename, xid_t xid, uint32_t flags, bool formatted)
132 {
133   struct vc_ctx_dlimit          limit;
134   
135   if (vc_get_dlimit(filename, xid, flags, &limit) == -1) {
136     perror(ENSC_WRAPPERS_PREFIX "vc_get_dlimit()");
137     exit(wrapper_exit_code);
138   }
139
140   if (formatted) {
141     writeInt (1, 0, xid);
142     WRITE_MSG(1, " ");
143     WRITE_STR(1, filename);
144     writeInt (1, "\nspace_used=",   limit.space_used);
145     writeInt (1, "\nspace_total=",  limit.space_total);
146     writeInt (1, "\ninodes_used=",  limit.inodes_used);
147     writeInt (1, "\ninodes_total=", limit.inodes_total);
148     writeInt (1, "\nreserved=",     limit.reserved);
149     WRITE_MSG(1, "\n");
150   }
151   else {
152     writeInt (1, 0,   xid);
153     writeInt (1, " ", limit.space_used);
154     writeInt (1, " ", limit.space_total);
155     writeInt (1, " ", limit.inodes_used);
156     writeInt (1, " ", limit.inodes_total);
157     writeInt (1, " ", limit.reserved);
158     WRITE_MSG(1, " ");
159     WRITE_STR(1, filename);
160     WRITE_MSG(1, "\n");
161   }
162 }
163
164
165 static bool
166 setDLimitField(struct vc_ctx_dlimit *dst, char const *opt)
167 {
168   uint_least32_t        *ptr;
169   char const * const    orig_opt = opt;
170
171 #define GET_VAL_PTR(CMP, VAL)                                           \
172   (strncmp(opt, CMP "=", sizeof(CMP))==0) ?                             \
173   (opt+=sizeof(CMP), &VAL) : 0
174   
175   if      ((ptr=GET_VAL_PTR("space_used",   dst->space_used))!=0)   {}
176   else if ((ptr=GET_VAL_PTR("space_total",  dst->space_total))!=0)  {}
177   else if ((ptr=GET_VAL_PTR("inodes_used",  dst->inodes_used))!=0)  {}
178   else if ((ptr=GET_VAL_PTR("inodes_total", dst->inodes_total))!=0) {}
179   else if ((ptr=GET_VAL_PTR("reserved",     dst->reserved))!=0)     {}
180   else      ptr=0;
181
182 #undef  GET_VAL_PTR  
183
184   if (ptr!=0 && *ptr==VC_CDLIM_KEEP) {
185     char        *endptr;
186     long        val = strtol(opt, &endptr, 0);
187
188     if (*opt==0 || *endptr!='\0') {
189       WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "can not parse number in '");
190       WRITE_STR(2, orig_opt);
191       WRITE_MSG(2, "'\n");
192       return false;
193     }
194
195     *ptr = val;
196   }
197   else if (ptr!=0) {
198     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "value already set in '");
199     WRITE_STR(2, orig_opt);
200     WRITE_MSG(2, "'\n");
201     return false;
202   }
203   else {
204     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "unknown limit in '");
205     WRITE_STR(2, orig_opt);
206     WRITE_MSG(2, "'\n");
207     return false;
208   }
209
210   return true;
211 }
212
213 bool
214 isHigherLimit(uint_least32_t lhs, uint_least32_t rhs)
215 {
216   if (lhs==VC_CDLIM_KEEP || rhs==VC_CDLIM_KEEP) return false;
217
218   return lhs > rhs;
219 }
220
221 int main(int argc, char *argv[])
222 {
223   bool          do_set       = false;
224   bool          do_remove    = false;
225   xid_t         xid          = VC_NOCTX;
226   uint32_t      flags        = 0;
227   char          *endptr;
228   int           sum          = 0;
229
230   struct vc_ctx_dlimit          limit = {
231     .space_used   = VC_CDLIM_KEEP,
232     .space_total  = VC_CDLIM_KEEP,
233     .inodes_used  = VC_CDLIM_KEEP,
234     .inodes_total = VC_CDLIM_KEEP,
235     .reserved     = VC_CDLIM_KEEP
236   };
237   
238   while (1) {
239     int         c = getopt_long(argc, argv, "+x:s:df:", CMDLINE_OPTIONS, 0);
240     if (c==-1) break;
241
242     switch (c) {
243       case CMD_HELP     :  showHelp(1, argv[0]);
244       case CMD_VERSION  :  showVersion();
245       case 'x'          :  xid = Evc_xidopt2xid(optarg, true); break;
246       case 's'          :
247         if (!setDLimitField(&limit, optarg))
248           return EXIT_FAILURE;
249         else
250           do_set = true;
251         break;
252       case 'd'          :  do_remove = true; break;
253       case 'f'          :
254         {
255           flags = strtol(optarg, &endptr, 0);
256           if ((flags == 0 && errno != 0) || *endptr != '\0') {
257             WRITE_MSG(2, "Invalid flags argument: '");
258             WRITE_STR(2, optarg);
259             WRITE_MSG(2, "'; try '--help' for more information\n");
260             return EXIT_FAILURE;
261           }
262         }
263         break;
264
265       default           :
266         WRITE_MSG(2, "Try '");
267         WRITE_STR(2, argv[0]);
268         WRITE_MSG(2, " --help' for more information.\n");
269         return EXIT_FAILURE;
270         break;
271     }
272   }
273
274   sum = ((do_set ? 1 : 0) + (do_remove ? 1 : 0));
275   
276   if (sum>1)
277     WRITE_MSG(2, "Can not specify multiple operations; try '--help' for more information\n");
278   else if (optind==argc)
279     WRITE_MSG(2, "No mount point specified; try '--help' for more information\n");
280   else if (xid==VC_NOCTX)
281     WRITE_MSG(2, "No xid specified; try '--help' for more information\n");
282   else if (isHigherLimit(limit.space_used, limit.space_total))
283     WRITE_MSG(2, "invalid parameters: 'space_used' is larger than 'space_total'\n");
284   else if (isHigherLimit(limit.inodes_used, limit.inodes_total))
285     WRITE_MSG(2, "invalid parameters: 'inodes_used' is larger than 'inodes_total'\n");
286   else {
287     for (; optind < argc; ++optind) {
288       if      (do_set)     setDlimit(argv[optind], xid, flags, &limit);
289       else if (do_remove)  remDlimit(argv[optind], xid, flags);
290       else                 printDlimit(argv[optind], xid, flags, true);
291     }
292
293     return EXIT_SUCCESS;
294   }
295
296   return EXIT_FAILURE;
297 }