blob: b08d4230068096ed3892c208931cc01b3410e339 [file] [log] [blame]
Denis Vlasenko9bff26c2006-10-20 19:40:44 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
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.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko83ea6432006-11-16 02:27:24 +000029/* Dependencies on runit_lib.c removed */
Denis Vlasenko9bff26c2006-10-20 19:40:44 +000030
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000031#include "libbb.h"
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +000032
Denis Vlasenkob3571492008-07-30 21:23:26 +000033/*
34Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +000035
Denis Vlasenkob3571492008-07-30 21:23:26 +000036Only softlimit and chpst are taking options:
37
38# common
39-o N Limit number of open files per process
40-p N Limit number of processes per uid
41-m BYTES Same as -d BYTES -s BYTES -l BYTES [-a BYTES]
42-d BYTES Limit data segment
43-f BYTES Limit output file sizes
44-c BYTES Limit core file size
45# softlimit
46-a BYTES Limit total size of all segments
47-s BYTES Limit stack segment
48-l BYTES Limit locked memory size
49-r BYTES Limit resident set size
50-t N Limit CPU time
51# chpst
52-u USER[:GRP] Set uid and gid
53-U USER[:GRP] Set $UID and $GID in environment
54-e DIR Set environment variables as specified by files in DIR
55-/ DIR Chroot to DIR
56-n NICE Add NICE to nice value
57-v Verbose
58-P Create new process group
59-0 -1 -2 Close fd 0,1,2
60
61Even though we accept all these options for both softlimit and chpst,
62they are not to be advertised on their help texts.
63We have enough problems with feature creep in other people's
64software, don't want to add our own.
65
66envdir, envuidgid, setuidgid take no options, but they reuse code which
67handles -e, -U and -u.
68*/
69
70enum {
71 OPT_a = (1 << 0) * ENABLE_SOFTLIMIT,
72 OPT_c = (1 << 1) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
73 OPT_d = (1 << 2) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
74 OPT_f = (1 << 3) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
75 OPT_l = (1 << 4) * ENABLE_SOFTLIMIT,
76 OPT_m = (1 << 5) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
77 OPT_o = (1 << 6) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
78 OPT_p = (1 << 7) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
79 OPT_r = (1 << 8) * ENABLE_SOFTLIMIT,
80 OPT_s = (1 << 9) * ENABLE_SOFTLIMIT,
81 OPT_t = (1 << 10) * ENABLE_SOFTLIMIT,
82 OPT_u = (1 << 11) * (ENABLE_CHPST || ENABLE_SETUIDGID),
83 OPT_U = (1 << 12) * (ENABLE_CHPST || ENABLE_ENVUIDGID),
84 OPT_e = (1 << 13) * (ENABLE_CHPST || ENABLE_ENVDIR),
85 OPT_root = (1 << 14) * ENABLE_CHPST,
86 OPT_n = (1 << 15) * ENABLE_CHPST,
87 OPT_v = (1 << 16) * ENABLE_CHPST,
88 OPT_P = (1 << 17) * ENABLE_CHPST,
89 OPT_0 = (1 << 18) * ENABLE_CHPST,
90 OPT_1 = (1 << 19) * ENABLE_CHPST,
91 OPT_2 = (1 << 20) * ENABLE_CHPST,
Denis Vlasenko23e3e252007-10-05 21:23:49 +000092};
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +000093
Bernhard Reutner-Fischerca254492009-10-26 23:27:07 +010094/* TODO: use recursive_action? */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +020095static NOINLINE void edir(const char *directory_name)
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +000096{
97 int wdir;
98 DIR *dir;
99 struct dirent *d;
100 int fd;
101
102 wdir = xopen(".", O_RDONLY | O_NDELAY);
103 xchdir(directory_name);
Bernhard Reutner-Fischerca254492009-10-26 23:27:07 +0100104 dir = xopendir(".");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000105 for (;;) {
Denys Vlasenko695fa512010-01-06 18:16:39 +0100106 char buf[256];
Denis Vlasenko9895dfd2008-04-01 16:13:14 +0000107 char *tail;
108 int size;
109
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000110 errno = 0;
111 d = readdir(dir);
112 if (!d) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000113 if (errno)
114 bb_perror_msg_and_die("readdir %s",
115 directory_name);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000116 break;
117 }
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000118 if (d->d_name[0] == '.')
119 continue;
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000120 fd = open(d->d_name, O_RDONLY | O_NDELAY);
121 if (fd < 0) {
Denis Vlasenkob3571492008-07-30 21:23:26 +0000122 if ((errno == EISDIR) && directory_name) {
123 if (option_mask32 & OPT_v)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000124 bb_perror_msg("warning: %s/%s is a directory",
125 directory_name, d->d_name);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000126 continue;
127 } else
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000128 bb_perror_msg_and_die("open %s/%s",
129 directory_name, d->d_name);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000130 }
Denis Vlasenko9895dfd2008-04-01 16:13:14 +0000131 size = full_read(fd, buf, sizeof(buf)-1);
132 close(fd);
133 if (size < 0)
134 bb_perror_msg_and_die("read %s/%s",
135 directory_name, d->d_name);
136 if (size == 0) {
137 unsetenv(d->d_name);
138 continue;
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000139 }
Denis Vlasenko9895dfd2008-04-01 16:13:14 +0000140 buf[size] = '\n';
141 tail = strchr(buf, '\n');
142 /* skip trailing whitespace */
143 while (1) {
144 *tail = '\0';
145 tail--;
146 if (tail < buf || !isspace(*tail))
147 break;
148 }
149 xsetenv(d->d_name, buf);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000150 }
151 closedir(dir);
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000152 if (fchdir(wdir) == -1)
153 bb_perror_msg_and_die("fchdir");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000154 close(wdir);
155}
156
157static void limit(int what, long l)
158{
159 struct rlimit r;
160
Denis Vlasenko4e6d5112008-03-12 22:14:34 +0000161 /* Never fails under Linux (except if you pass it bad arguments) */
162 getrlimit(what, &r);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000163 if ((l < 0) || (l > r.rlim_max))
164 r.rlim_cur = r.rlim_max;
165 else
166 r.rlim_cur = l;
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000167 if (setrlimit(what, &r) == -1)
168 bb_perror_msg_and_die("setrlimit");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000169}
170
Denis Vlasenkob3571492008-07-30 21:23:26 +0000171int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
172int chpst_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000173{
Denis Vlasenkob3571492008-07-30 21:23:26 +0000174 struct bb_uidgid_t ugid;
Denis Vlasenkoec73d302008-08-05 17:43:29 +0000175 char *set_user = set_user; /* for compiler */
176 char *env_user = env_user;
177 char *env_dir = env_dir;
Denis Vlasenkob3571492008-07-30 21:23:26 +0000178 char *root;
179 char *nicestr;
180 unsigned limita;
181 unsigned limitc;
182 unsigned limitd;
183 unsigned limitf;
184 unsigned limitl;
185 unsigned limitm;
186 unsigned limito;
187 unsigned limitp;
188 unsigned limitr;
189 unsigned limits;
190 unsigned limitt;
191 unsigned opt;
192
193 if ((ENABLE_CHPST && applet_name[0] == 'c')
194 || (ENABLE_SOFTLIMIT && applet_name[1] == 'o')
195 ) {
196 // FIXME: can we live with int-sized limits?
197 // can we live with 40000 days?
198 // if yes -> getopt converts strings to numbers for us
199 opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+";
200 opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000201 IF_CHPST("/:n:vP012"),
Denis Vlasenkob3571492008-07-30 21:23:26 +0000202 &limita, &limitc, &limitd, &limitf, &limitl,
203 &limitm, &limito, &limitp, &limitr, &limits, &limitt,
204 &set_user, &env_user, &env_dir
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000205 IF_CHPST(, &root, &nicestr));
Denis Vlasenkob3571492008-07-30 21:23:26 +0000206 argv += optind;
207 if (opt & OPT_m) { // -m means -asld
208 limita = limits = limitl = limitd = limitm;
209 opt |= (OPT_s | OPT_l | OPT_a | OPT_d);
210 }
211 } else {
212 option_mask32 = opt = 0;
213 argv++;
Denis Vlasenkob9c262b2008-08-20 22:19:27 +0000214 if (!*argv)
215 bb_show_usage();
Denis Vlasenkob3571492008-07-30 21:23:26 +0000216 }
217
218 // envdir?
219 if (ENABLE_ENVDIR && applet_name[3] == 'd') {
220 env_dir = *argv++;
221 opt |= OPT_e;
222 }
223
224 // setuidgid?
Denis Vlasenkob9c262b2008-08-20 22:19:27 +0000225 if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
Denis Vlasenkob3571492008-07-30 21:23:26 +0000226 set_user = *argv++;
227 opt |= OPT_u;
228 }
229
230 // envuidgid?
Denis Vlasenko69ed3092008-08-15 21:03:17 +0000231 if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
Denis Vlasenkob3571492008-07-30 21:23:26 +0000232 env_user = *argv++;
233 opt |= OPT_U;
234 }
235
236 // we must have PROG [ARGS]
237 if (!*argv)
238 bb_show_usage();
239
240 // set limits
241 if (opt & OPT_d) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000242#ifdef RLIMIT_DATA
243 limit(RLIMIT_DATA, limitd);
244#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000245 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000246 bb_error_msg("system does not support RLIMIT_%s",
247 "DATA");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000248#endif
249 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000250 if (opt & OPT_s) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000251#ifdef RLIMIT_STACK
252 limit(RLIMIT_STACK, limits);
253#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000254 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000255 bb_error_msg("system does not support RLIMIT_%s",
256 "STACK");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000257#endif
258 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000259 if (opt & OPT_l) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000260#ifdef RLIMIT_MEMLOCK
261 limit(RLIMIT_MEMLOCK, limitl);
262#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000263 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000264 bb_error_msg("system does not support RLIMIT_%s",
265 "MEMLOCK");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000266#endif
267 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000268 if (opt & OPT_a) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000269#ifdef RLIMIT_VMEM
270 limit(RLIMIT_VMEM, limita);
271#else
272#ifdef RLIMIT_AS
273 limit(RLIMIT_AS, limita);
274#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000275 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000276 bb_error_msg("system does not support RLIMIT_%s",
277 "VMEM");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000278#endif
279#endif
280 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000281 if (opt & OPT_o) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000282#ifdef RLIMIT_NOFILE
283 limit(RLIMIT_NOFILE, limito);
284#else
285#ifdef RLIMIT_OFILE
286 limit(RLIMIT_OFILE, limito);
287#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000288 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000289 bb_error_msg("system does not support RLIMIT_%s",
290 "NOFILE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000291#endif
292#endif
293 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000294 if (opt & OPT_p) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000295#ifdef RLIMIT_NPROC
296 limit(RLIMIT_NPROC, limitp);
297#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000298 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000299 bb_error_msg("system does not support RLIMIT_%s",
300 "NPROC");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000301#endif
302 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000303 if (opt & OPT_f) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000304#ifdef RLIMIT_FSIZE
305 limit(RLIMIT_FSIZE, limitf);
306#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000307 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000308 bb_error_msg("system does not support RLIMIT_%s",
309 "FSIZE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000310#endif
311 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000312 if (opt & OPT_c) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000313#ifdef RLIMIT_CORE
314 limit(RLIMIT_CORE, limitc);
315#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000316 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000317 bb_error_msg("system does not support RLIMIT_%s",
318 "CORE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000319#endif
320 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000321 if (opt & OPT_r) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000322#ifdef RLIMIT_RSS
323 limit(RLIMIT_RSS, limitr);
324#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000325 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000326 bb_error_msg("system does not support RLIMIT_%s",
327 "RSS");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000328#endif
329 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000330 if (opt & OPT_t) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000331#ifdef RLIMIT_CPU
332 limit(RLIMIT_CPU, limitt);
333#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000334 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000335 bb_error_msg("system does not support RLIMIT_%s",
336 "CPU");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000337#endif
338 }
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000339
Denis Vlasenkob3571492008-07-30 21:23:26 +0000340 if (opt & OPT_P)
341 setsid();
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000342
Denis Vlasenkob3571492008-07-30 21:23:26 +0000343 if (opt & OPT_e)
344 edir(env_dir);
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000345
Denis Vlasenkob3571492008-07-30 21:23:26 +0000346 // FIXME: chrooted jail must have /etc/passwd if we move this after chroot!
347 // OTOH chroot fails for non-roots!
348 // SOLUTION: cache uid/gid before chroot, apply uid/gid after
349 if (opt & OPT_U) {
350 xget_uidgid(&ugid, env_user);
351 xsetenv("GID", utoa(ugid.gid));
352 xsetenv("UID", utoa(ugid.uid));
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000353 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000354
Denis Vlasenkob3571492008-07-30 21:23:26 +0000355 if (opt & OPT_u) {
356 xget_uidgid(&ugid, set_user);
357 }
358
359 if (opt & OPT_root) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000360 xchdir(root);
Denis Vlasenko394eebe2008-02-25 20:30:24 +0000361 xchroot(".");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000362 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000363
364 if (opt & OPT_u) {
365 if (setgroups(1, &ugid.gid) == -1)
366 bb_perror_msg_and_die("setgroups");
367 xsetgid(ugid.gid);
368 xsetuid(ugid.uid);
369 }
370
371 if (opt & OPT_n) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000372 errno = 0;
Denis Vlasenkob3571492008-07-30 21:23:26 +0000373 if (nice(xatoi(nicestr)) == -1)
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000374 bb_perror_msg_and_die("nice");
375 }
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000376
Denis Vlasenkob3571492008-07-30 21:23:26 +0000377 if (opt & OPT_0)
378 close(STDIN_FILENO);
379 if (opt & OPT_1)
380 close(STDOUT_FILENO);
381 if (opt & OPT_2)
382 close(STDERR_FILENO);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000383
Pascal Bellard21e8e8d2010-07-04 00:57:03 +0200384 BB_EXECVP_or_die(argv);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000385}