merge with 0.30.213
[util-vserver.git] / src / vsched.c
1 // $Id: vsched.c 2510 2007-03-07 20:33:56Z dhozac $    --*- c -*--
2
3 // Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // Copyright (C) 2006 Daniel Hokka Zakrisson <daniel@hozac.com>
5 //  
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; version 2 of the License.
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 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif
23
24 #include "util.h"
25 #include "vserver.h"
26
27 #include <errno.h>
28 #include <unistd.h>
29 #include <getopt.h>
30 #include <libgen.h>
31 #include <dirent.h>
32 #include <fcntl.h>
33 #include <sys/stat.h>
34 #include <stddef.h>
35
36 #define ENSC_WRAPPERS_PREFIX    "vsched: "
37 #define ENSC_WRAPPERS_VSERVER   1
38 #define ENSC_WRAPPERS_UNISTD    1
39 #define ENSC_WRAPPERS_FCNTL     1
40 #define ENSC_WRAPPERS_DIRENT    1
41 #define ENSC_WRAPPERS_STAT      1
42 #include <wrappers.h>
43
44 #define CMD_HELP                0x1000
45 #define CMD_VERSION             0x1001
46 #define CMD_XID                 0x4000
47 #define CMD_FRATE               0x4001
48 #define CMD_INTERVAL            0x4002
49 #define CMD_TOKENS              0x4003
50 #define CMD_TOK_MIN             0x4004
51 #define CMD_TOK_MAX             0x4005
52 #define CMD_CPU_MASK            0x4006
53 #define CMD_PRIO_BIAS           0x4007
54 #define CMD_FRATE2              0x4008
55 #define CMD_INTERVAL2           0x4009
56 #define CMD_CPUID               0x400a
57 #define CMD_BUCKETID            0x400b
58 #define CMD_FORCE               0x400c
59 #define CMD_IDLE_TIME           0x400d
60 #define CMD_DIR                 0x400e
61 #define CMD_MISSING             0x400f
62
63 int                     wrapper_exit_code = 255;
64
65 struct option const
66 CMDLINE_OPTIONS[] = {
67   { "help",     no_argument,  0, CMD_HELP },
68   { "version",  no_argument,  0, CMD_VERSION },
69   { "ctx",           required_argument, 0, CMD_XID },
70   { "xid",           required_argument, 0, CMD_XID },
71   { "fill-rate",     required_argument, 0, CMD_FRATE },
72   { "interval",      required_argument, 0, CMD_INTERVAL },
73   { "tokens",        required_argument, 0, CMD_TOKENS },
74   { "tokens_min",    required_argument, 0, CMD_TOK_MIN },
75   { "tokens-min",    required_argument, 0, CMD_TOK_MIN },
76   { "tokens_max",    required_argument, 0, CMD_TOK_MAX },
77   { "tokens-max",    required_argument, 0, CMD_TOK_MAX },
78   { "prio_bias",     required_argument, 0, CMD_PRIO_BIAS },
79   { "prio-bias",     required_argument, 0, CMD_PRIO_BIAS },
80   { "priority_bias", required_argument, 0, CMD_PRIO_BIAS },
81   { "priority-bias", required_argument, 0, CMD_PRIO_BIAS },
82   { "cpu_mask",      required_argument, 0, CMD_CPU_MASK },
83   { "fill-rate2",    required_argument, 0, CMD_FRATE2 },
84   { "interval2",     required_argument, 0, CMD_INTERVAL2 },
85   { "cpu-id",        required_argument, 0, CMD_CPUID },
86   { "bucket-id",     required_argument, 0, CMD_BUCKETID },
87   { "force",         no_argument,       0, CMD_FORCE },
88   { "idle-time",     no_argument,       0, CMD_IDLE_TIME },
89   { "dir",           required_argument, 0, CMD_DIR },
90   { "missingok",     no_argument,       0, CMD_MISSING },
91   {0,0,0,0}
92 };
93
94 struct sched_opt {
95   const char * const    name;
96   uint_least32_t        mask;
97   size_t                offset;
98 };
99 #define FOPT(NAME,MASK,FIELD)   { #NAME, MASK, offsetof(struct vc_set_sched, FIELD) }
100 static struct sched_opt FILE_OPTIONS[] = {
101   FOPT(fill-rate,       VC_VXSM_FILL_RATE,                      fill_rate),
102   FOPT(interval,        VC_VXSM_INTERVAL,                       interval),
103   FOPT(tokens,          VC_VXSM_TOKENS,                         tokens),
104   FOPT(tokens-min,      VC_VXSM_TOKENS_MIN,                     tokens_min),
105   FOPT(tokens-max,      VC_VXSM_TOKENS_MAX,                     tokens_max),
106   FOPT(prio-bias,       VC_VXSM_PRIO_BIAS,                      priority_bias),
107   FOPT(priority-bias,   VC_VXSM_PRIO_BIAS,                      priority_bias),
108   FOPT(fill-rate2,      VC_VXSM_FILL_RATE2|VC_VXSM_IDLE_TIME,   fill_rate2),
109   FOPT(interval2,       VC_VXSM_INTERVAL2|VC_VXSM_IDLE_TIME,    interval2),
110   FOPT(cpu-id,          VC_VXSM_CPU_ID,                         cpu_id),
111   FOPT(bucket-id,       VC_VXSM_BUCKET_ID,                      bucket_id),
112   FOPT(idle-time,       VC_VXSM_IDLE_TIME,                      set_mask),
113   {0,0,0}
114 };
115
116 static void
117 showHelp(int fd, char const *cmd, int res)
118 {
119   VSERVER_DECLARE_CMD(cmd);
120
121   WRITE_MSG(fd, "Usage:\n  ");
122   WRITE_STR(fd, cmd);
123   WRITE_MSG(fd,
124             " [--xid <xid>] <sched options>* [--dir <dir>] [--] [<command> <args>*]\n"
125             "\n"
126             "Options:\n"
127             "    --fill-rate <rate>\n"
128             "    --interval <interval>\n"
129             "    --tokens <tokens>\n"
130             "    --tokens-min <tokens>\n"
131             "    --tokens-max <tokens>\n"
132             "    --prio-bias <bias>\n"
133             "    --fill-rate2 <rate>\n"
134             "    --interval2 <interval>\n"
135             "    --cpu-id <CPU id>\n"
136             "    --bucket-id <bucket id>\n"
137             "    --idle-time    ...  set the idle time flag; this is required for\n"
138             "                        all updates to the scheduler to keep it enabled\n"
139             "    --force        ...  force update of all per-CPU schedulers now\n"
140             "    --dir <dir>    ...  read settings from <dir>\n"
141             "    --missingok    ...  do not fail when <dir> does not exist\n"
142             "\nPlease report bugs to " PACKAGE_BUGREPORT "\n");
143
144   exit(res);
145 }
146
147 static void
148 showVersion()
149 {
150   WRITE_MSG(1,
151             "vsched " VERSION " -- modifies scheduling parameters\n"
152             "This program is part of " PACKAGE_STRING "\n\n"
153             "Copyright (C) 2003,2004 Enrico Scholz\n"
154             "Copyright (C) 2006 Daniel Hokka Zakrisson\n"
155             VERSION_COPYRIGHT_DISCLAIMER);
156   exit(0);
157 }
158
159 static void do_dir_entry(struct vc_set_sched *sched, const char *name)
160 {
161   struct sched_opt *opt;
162
163   for (opt = FILE_OPTIONS; opt->name != 0; opt++) {
164     if (strcmp(name, opt->name) == 0)
165       break;
166   }
167   if (opt->name == 0)
168     return;
169
170   if (opt->offset != offsetof(struct vc_set_sched, set_mask)) {
171     int fd;
172     char buf[128], *newline;
173     signed long val;
174     ssize_t len;
175
176     fd = Eopen(name, O_RDONLY, 0);
177     len = Eread(fd, buf, sizeof(buf)-1);
178     Eclose(fd);
179     buf[len] = '\0';
180     if ((newline=strchr(buf, '\n')) != NULL)
181       *newline = '\0';
182
183     if (!isNumber(buf, &val, true)) {
184       WRITE_MSG(2, ENSC_WRAPPERS_PREFIX);
185       WRITE_STR(2, name);
186       WRITE_MSG(2, ": is not a number\n");
187       exit(1);
188     }
189
190     *(int_least32_t *)(((char *)sched)+opt->offset) = (int_least32_t) val;
191   }
192
193   sched->set_mask |= opt->mask;
194 }
195
196 static void do_dir(xid_t xid, struct vc_set_sched *sched, const char *dir, int missing_ok, int per_cpu)
197 {
198   DIR                   *dp;
199   struct dirent         *de;
200   int                   cur_fd = Eopen(".", O_RDONLY, 0);
201   struct stat           st;
202
203   if (chdir(dir)!=-1) {
204     dp = Eopendir(".");
205     while ((de = Ereaddir(dp)) != NULL) {
206       if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
207         continue;
208       Estat(de->d_name, &st);
209       if (S_ISDIR(st.st_mode))
210         continue;
211       do_dir_entry(sched, de->d_name);
212     }
213
214     /* set the values now */
215     if (vc_set_sched(xid, sched) == -1) {
216       perror(ENSC_WRAPPERS_PREFIX "vc_set_sched()");
217       exit(1);
218     }
219
220     if (!per_cpu) {
221       struct vc_set_sched per_cpu_sched;
222
223       rewinddir(dp);
224       while ((de = Ereaddir(dp)) != NULL) {
225         if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))
226           continue;
227         Estat(de->d_name, &st);
228         if (S_ISDIR(st.st_mode)) {
229           per_cpu_sched.set_mask = sched->set_mask & (VC_VXSM_IDLE_TIME|VC_VXSM_FORCE);
230           do_dir(xid, &per_cpu_sched, de->d_name, 0, 1);
231         }
232       }
233     }
234
235     Eclosedir(dp);
236   }
237   else if (!missing_ok) {
238     perror(ENSC_WRAPPERS_PREFIX "chdir()");
239     exit(wrapper_exit_code);
240   }
241
242   Efchdir(cur_fd);
243 }
244
245 #define SETVAL(ATTR,MASK) \
246   if (!isNumber(optarg, &tmp, false)) { \
247     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "non-numeric value specified for '--" #ATTR "'\n"); \
248     exit(wrapper_exit_code); \
249   } \
250   else { \
251     sched.ATTR      = tmp; \
252     sched.set_mask |= MASK; \
253   }
254
255 int main(int argc, char *argv[])
256 {
257   xid_t                 xid   = VC_NOCTX;
258   signed long           tmp;
259   struct vc_set_sched   sched = {
260     .set_mask = 0
261   };
262   const char            *dir = NULL;
263   int                   missing_ok = 0;
264   
265   while (1) {
266     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
267     if (c==-1) break;
268
269     switch (c) {
270       case CMD_HELP     :  showHelp(1, argv[0], 0);
271       case CMD_VERSION  :  showVersion();
272       case CMD_XID      :  xid = Evc_xidopt2xid(optarg,true);         break;
273       case CMD_FRATE    :  SETVAL(fill_rate,     VC_VXSM_FILL_RATE);  break;
274       case CMD_INTERVAL :  SETVAL(interval,      VC_VXSM_INTERVAL);   break;
275       case CMD_TOKENS   :  SETVAL(tokens,        VC_VXSM_TOKENS);     break;
276       case CMD_TOK_MIN  :  SETVAL(tokens_min,    VC_VXSM_TOKENS_MIN); break;
277       case CMD_TOK_MAX  :  SETVAL(tokens_max,    VC_VXSM_TOKENS_MAX); break;
278       case CMD_PRIO_BIAS:  SETVAL(priority_bias, VC_VXSM_PRIO_BIAS);  break;
279       case CMD_CPU_MASK :
280         WRITE_MSG(2, "vsched: WARNING: the '--cpu_mask' parameter is deprecated and will not have any effect\n");
281         break;
282       case CMD_FRATE2   :  SETVAL(fill_rate2,    VC_VXSM_FILL_RATE2); break;
283       case CMD_INTERVAL2:  SETVAL(interval2,     VC_VXSM_INTERVAL2);  break;
284       case CMD_CPUID    :  SETVAL(cpu_id,        VC_VXSM_CPU_ID);     break;
285       case CMD_BUCKETID :  SETVAL(bucket_id,     VC_VXSM_BUCKET_ID);  break;
286       case CMD_DIR      :  dir = optarg;                              break;
287       case CMD_MISSING  :  missing_ok = 1;                            break;
288       case CMD_FORCE    :  sched.set_mask |= VC_VXSM_FORCE;           break;
289       case CMD_IDLE_TIME:  sched.set_mask |= VC_VXSM_IDLE_TIME;       break;
290       default           :
291         WRITE_MSG(2, "Try '");
292         WRITE_STR(2, argv[0]);
293         WRITE_MSG(2, " --help' for more information.\n");
294         return EXIT_FAILURE;
295         break;
296     }
297   }
298
299   if (xid==VC_NOCTX && optind==argc) {
300     WRITE_MSG(2, "Without a program, '--xid' must be used; try '--help' for more information\n");
301     exit(wrapper_exit_code);
302   }
303
304   if (sched.set_mask==0 && dir==NULL && optind==argc) {
305     WRITE_MSG(2, "Neither an option nor a program was specified; try '--help' for more information\n");
306     exit(wrapper_exit_code);
307   }
308
309   if (xid==VC_NOCTX)
310     xid = Evc_get_task_xid(0);
311
312   if (dir) {
313     do_dir(xid, &sched, dir, missing_ok, 0);
314   }
315   else {
316     if (sched.set_mask!=0 && vc_set_sched(xid, &sched)==-1) {
317       perror("vc_set_sched()");
318       exit(255);
319     }
320   }
321
322   if (optind<argc)
323     EexecvpD(argv[optind],argv+optind);
324
325   return EXIT_SUCCESS;
326 }