Tomato 1.25
[tomato.git] / release / src / router / busybox / runit / chpst.c
blob82a81f5492235a15da76adb5af7c76f2425d32bc
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* Dependencies on runit_lib.c removed */
31 #include "libbb.h"
32 #include <dirent.h>
35 Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
37 Only softlimit and chpst are taking options:
39 # common
40 -o N Limit number of open files per process
41 -p N Limit number of processes per uid
42 -m BYTES Same as -d BYTES -s BYTES -l BYTES [-a BYTES]
43 -d BYTES Limit data segment
44 -f BYTES Limit output file sizes
45 -c BYTES Limit core file size
46 # softlimit
47 -a BYTES Limit total size of all segments
48 -s BYTES Limit stack segment
49 -l BYTES Limit locked memory size
50 -r BYTES Limit resident set size
51 -t N Limit CPU time
52 # chpst
53 -u USER[:GRP] Set uid and gid
54 -U USER[:GRP] Set $UID and $GID in environment
55 -e DIR Set environment variables as specified by files in DIR
56 -/ DIR Chroot to DIR
57 -n NICE Add NICE to nice value
58 -v Verbose
59 -P Create new process group
60 -0 -1 -2 Close fd 0,1,2
62 Even though we accept all these options for both softlimit and chpst,
63 they are not to be advertised on their help texts.
64 We have enough problems with feature creep in other people's
65 software, don't want to add our own.
67 envdir, envuidgid, setuidgid take no options, but they reuse code which
68 handles -e, -U and -u.
71 enum {
72 OPT_a = (1 << 0) * ENABLE_SOFTLIMIT,
73 OPT_c = (1 << 1) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
74 OPT_d = (1 << 2) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
75 OPT_f = (1 << 3) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
76 OPT_l = (1 << 4) * ENABLE_SOFTLIMIT,
77 OPT_m = (1 << 5) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
78 OPT_o = (1 << 6) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
79 OPT_p = (1 << 7) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
80 OPT_r = (1 << 8) * ENABLE_SOFTLIMIT,
81 OPT_s = (1 << 9) * ENABLE_SOFTLIMIT,
82 OPT_t = (1 << 10) * ENABLE_SOFTLIMIT,
83 OPT_u = (1 << 11) * (ENABLE_CHPST || ENABLE_SETUIDGID),
84 OPT_U = (1 << 12) * (ENABLE_CHPST || ENABLE_ENVUIDGID),
85 OPT_e = (1 << 13) * (ENABLE_CHPST || ENABLE_ENVDIR),
86 OPT_root = (1 << 14) * ENABLE_CHPST,
87 OPT_n = (1 << 15) * ENABLE_CHPST,
88 OPT_v = (1 << 16) * ENABLE_CHPST,
89 OPT_P = (1 << 17) * ENABLE_CHPST,
90 OPT_0 = (1 << 18) * ENABLE_CHPST,
91 OPT_1 = (1 << 19) * ENABLE_CHPST,
92 OPT_2 = (1 << 20) * ENABLE_CHPST,
95 static void edir(const char *directory_name)
97 int wdir;
98 DIR *dir;
99 struct dirent *d;
100 int fd;
102 wdir = xopen(".", O_RDONLY | O_NDELAY);
103 xchdir(directory_name);
104 dir = opendir(".");
105 if (!dir)
106 bb_perror_msg_and_die("opendir %s", directory_name);
107 for (;;) {
108 char buf[256];
109 char *tail;
110 int size;
112 errno = 0;
113 d = readdir(dir);
114 if (!d) {
115 if (errno)
116 bb_perror_msg_and_die("readdir %s",
117 directory_name);
118 break;
120 if (d->d_name[0] == '.')
121 continue;
122 fd = open(d->d_name, O_RDONLY | O_NDELAY);
123 if (fd < 0) {
124 if ((errno == EISDIR) && directory_name) {
125 if (option_mask32 & OPT_v)
126 bb_perror_msg("warning: %s/%s is a directory",
127 directory_name, d->d_name);
128 continue;
129 } else
130 bb_perror_msg_and_die("open %s/%s",
131 directory_name, d->d_name);
133 size = full_read(fd, buf, sizeof(buf)-1);
134 close(fd);
135 if (size < 0)
136 bb_perror_msg_and_die("read %s/%s",
137 directory_name, d->d_name);
138 if (size == 0) {
139 unsetenv(d->d_name);
140 continue;
142 buf[size] = '\n';
143 tail = strchr(buf, '\n');
144 /* skip trailing whitespace */
145 while (1) {
146 *tail = '\0';
147 tail--;
148 if (tail < buf || !isspace(*tail))
149 break;
151 xsetenv(d->d_name, buf);
153 closedir(dir);
154 if (fchdir(wdir) == -1)
155 bb_perror_msg_and_die("fchdir");
156 close(wdir);
159 static void limit(int what, long l)
161 struct rlimit r;
163 /* Never fails under Linux (except if you pass it bad arguments) */
164 getrlimit(what, &r);
165 if ((l < 0) || (l > r.rlim_max))
166 r.rlim_cur = r.rlim_max;
167 else
168 r.rlim_cur = l;
169 if (setrlimit(what, &r) == -1)
170 bb_perror_msg_and_die("setrlimit");
173 int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
174 int chpst_main(int argc UNUSED_PARAM, char **argv)
176 struct bb_uidgid_t ugid;
177 char *set_user = set_user; /* for compiler */
178 char *env_user = env_user;
179 char *env_dir = env_dir;
180 char *root;
181 char *nicestr;
182 unsigned limita;
183 unsigned limitc;
184 unsigned limitd;
185 unsigned limitf;
186 unsigned limitl;
187 unsigned limitm;
188 unsigned limito;
189 unsigned limitp;
190 unsigned limitr;
191 unsigned limits;
192 unsigned limitt;
193 unsigned opt;
195 if ((ENABLE_CHPST && applet_name[0] == 'c')
196 || (ENABLE_SOFTLIMIT && applet_name[1] == 'o')
198 // FIXME: can we live with int-sized limits?
199 // can we live with 40000 days?
200 // if yes -> getopt converts strings to numbers for us
201 opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+";
202 opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:"
203 USE_CHPST("/:n:vP012"),
204 &limita, &limitc, &limitd, &limitf, &limitl,
205 &limitm, &limito, &limitp, &limitr, &limits, &limitt,
206 &set_user, &env_user, &env_dir
207 USE_CHPST(, &root, &nicestr));
208 argv += optind;
209 if (opt & OPT_m) { // -m means -asld
210 limita = limits = limitl = limitd = limitm;
211 opt |= (OPT_s | OPT_l | OPT_a | OPT_d);
213 } else {
214 option_mask32 = opt = 0;
215 argv++;
216 if (!*argv)
217 bb_show_usage();
220 // envdir?
221 if (ENABLE_ENVDIR && applet_name[3] == 'd') {
222 env_dir = *argv++;
223 opt |= OPT_e;
226 // setuidgid?
227 if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
228 set_user = *argv++;
229 opt |= OPT_u;
232 // envuidgid?
233 if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
234 env_user = *argv++;
235 opt |= OPT_U;
238 // we must have PROG [ARGS]
239 if (!*argv)
240 bb_show_usage();
242 // set limits
243 if (opt & OPT_d) {
244 #ifdef RLIMIT_DATA
245 limit(RLIMIT_DATA, limitd);
246 #else
247 if (opt & OPT_v)
248 bb_error_msg("system does not support RLIMIT_%s",
249 "DATA");
250 #endif
252 if (opt & OPT_s) {
253 #ifdef RLIMIT_STACK
254 limit(RLIMIT_STACK, limits);
255 #else
256 if (opt & OPT_v)
257 bb_error_msg("system does not support RLIMIT_%s",
258 "STACK");
259 #endif
261 if (opt & OPT_l) {
262 #ifdef RLIMIT_MEMLOCK
263 limit(RLIMIT_MEMLOCK, limitl);
264 #else
265 if (opt & OPT_v)
266 bb_error_msg("system does not support RLIMIT_%s",
267 "MEMLOCK");
268 #endif
270 if (opt & OPT_a) {
271 #ifdef RLIMIT_VMEM
272 limit(RLIMIT_VMEM, limita);
273 #else
274 #ifdef RLIMIT_AS
275 limit(RLIMIT_AS, limita);
276 #else
277 if (opt & OPT_v)
278 bb_error_msg("system does not support RLIMIT_%s",
279 "VMEM");
280 #endif
281 #endif
283 if (opt & OPT_o) {
284 #ifdef RLIMIT_NOFILE
285 limit(RLIMIT_NOFILE, limito);
286 #else
287 #ifdef RLIMIT_OFILE
288 limit(RLIMIT_OFILE, limito);
289 #else
290 if (opt & OPT_v)
291 bb_error_msg("system does not support RLIMIT_%s",
292 "NOFILE");
293 #endif
294 #endif
296 if (opt & OPT_p) {
297 #ifdef RLIMIT_NPROC
298 limit(RLIMIT_NPROC, limitp);
299 #else
300 if (opt & OPT_v)
301 bb_error_msg("system does not support RLIMIT_%s",
302 "NPROC");
303 #endif
305 if (opt & OPT_f) {
306 #ifdef RLIMIT_FSIZE
307 limit(RLIMIT_FSIZE, limitf);
308 #else
309 if (opt & OPT_v)
310 bb_error_msg("system does not support RLIMIT_%s",
311 "FSIZE");
312 #endif
314 if (opt & OPT_c) {
315 #ifdef RLIMIT_CORE
316 limit(RLIMIT_CORE, limitc);
317 #else
318 if (opt & OPT_v)
319 bb_error_msg("system does not support RLIMIT_%s",
320 "CORE");
321 #endif
323 if (opt & OPT_r) {
324 #ifdef RLIMIT_RSS
325 limit(RLIMIT_RSS, limitr);
326 #else
327 if (opt & OPT_v)
328 bb_error_msg("system does not support RLIMIT_%s",
329 "RSS");
330 #endif
332 if (opt & OPT_t) {
333 #ifdef RLIMIT_CPU
334 limit(RLIMIT_CPU, limitt);
335 #else
336 if (opt & OPT_v)
337 bb_error_msg("system does not support RLIMIT_%s",
338 "CPU");
339 #endif
342 if (opt & OPT_P)
343 setsid();
345 if (opt & OPT_e)
346 edir(env_dir);
348 // FIXME: chrooted jail must have /etc/passwd if we move this after chroot!
349 // OTOH chroot fails for non-roots!
350 // SOLUTION: cache uid/gid before chroot, apply uid/gid after
351 if (opt & OPT_U) {
352 xget_uidgid(&ugid, env_user);
353 xsetenv("GID", utoa(ugid.gid));
354 xsetenv("UID", utoa(ugid.uid));
357 if (opt & OPT_u) {
358 xget_uidgid(&ugid, set_user);
361 if (opt & OPT_root) {
362 xchdir(root);
363 xchroot(".");
366 if (opt & OPT_u) {
367 if (setgroups(1, &ugid.gid) == -1)
368 bb_perror_msg_and_die("setgroups");
369 xsetgid(ugid.gid);
370 xsetuid(ugid.uid);
373 if (opt & OPT_n) {
374 errno = 0;
375 if (nice(xatoi(nicestr)) == -1)
376 bb_perror_msg_and_die("nice");
379 if (opt & OPT_0)
380 close(STDIN_FILENO);
381 if (opt & OPT_1)
382 close(STDOUT_FILENO);
383 if (opt & OPT_2)
384 close(STDERR_FILENO);
386 BB_EXECVP(argv[0], argv);
387 bb_perror_msg_and_die("exec %s", argv[0]);