blob: 1a68eb755320951efa881cfbeb96cb06adc5dcd5 [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#include <dirent.h>
33
Denis Vlasenkob3571492008-07-30 21:23:26 +000034/*
35Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +000036
Denis Vlasenkob3571492008-07-30 21:23:26 +000037Only softlimit and chpst are taking options:
38
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
61
62Even though we accept all these options for both softlimit and chpst,
63they are not to be advertised on their help texts.
64We have enough problems with feature creep in other people's
65software, don't want to add our own.
66
67envdir, envuidgid, setuidgid take no options, but they reuse code which
68handles -e, -U and -u.
69*/
70
71enum {
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,
Denis Vlasenko23e3e252007-10-05 21:23:49 +000093};
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +000094
Bernhard Reutner-Fischerca254492009-10-26 23:27:07 +010095/* TODO: use recursive_action? */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +020096static NOINLINE void edir(const char *directory_name)
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +000097{
98 int wdir;
99 DIR *dir;
100 struct dirent *d;
101 int fd;
102
103 wdir = xopen(".", O_RDONLY | O_NDELAY);
104 xchdir(directory_name);
Bernhard Reutner-Fischerca254492009-10-26 23:27:07 +0100105 dir = xopendir(".");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000106 for (;;) {
Denys Vlasenko695fa512010-01-06 18:16:39 +0100107 char buf[256];
Denis Vlasenko9895dfd2008-04-01 16:13:14 +0000108 char *tail;
109 int size;
110
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000111 errno = 0;
112 d = readdir(dir);
113 if (!d) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000114 if (errno)
115 bb_perror_msg_and_die("readdir %s",
116 directory_name);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000117 break;
118 }
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000119 if (d->d_name[0] == '.')
120 continue;
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000121 fd = open(d->d_name, O_RDONLY | O_NDELAY);
122 if (fd < 0) {
Denis Vlasenkob3571492008-07-30 21:23:26 +0000123 if ((errno == EISDIR) && directory_name) {
124 if (option_mask32 & OPT_v)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000125 bb_perror_msg("warning: %s/%s is a directory",
126 directory_name, d->d_name);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000127 continue;
128 } else
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000129 bb_perror_msg_and_die("open %s/%s",
130 directory_name, d->d_name);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000131 }
Denis Vlasenko9895dfd2008-04-01 16:13:14 +0000132 size = full_read(fd, buf, sizeof(buf)-1);
133 close(fd);
134 if (size < 0)
135 bb_perror_msg_and_die("read %s/%s",
136 directory_name, d->d_name);
137 if (size == 0) {
138 unsetenv(d->d_name);
139 continue;
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000140 }
Denis Vlasenko9895dfd2008-04-01 16:13:14 +0000141 buf[size] = '\n';
142 tail = strchr(buf, '\n');
143 /* skip trailing whitespace */
144 while (1) {
145 *tail = '\0';
146 tail--;
147 if (tail < buf || !isspace(*tail))
148 break;
149 }
150 xsetenv(d->d_name, buf);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000151 }
152 closedir(dir);
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000153 if (fchdir(wdir) == -1)
154 bb_perror_msg_and_die("fchdir");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000155 close(wdir);
156}
157
158static void limit(int what, long l)
159{
160 struct rlimit r;
161
Denis Vlasenko4e6d5112008-03-12 22:14:34 +0000162 /* Never fails under Linux (except if you pass it bad arguments) */
163 getrlimit(what, &r);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000164 if ((l < 0) || (l > r.rlim_max))
165 r.rlim_cur = r.rlim_max;
166 else
167 r.rlim_cur = l;
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000168 if (setrlimit(what, &r) == -1)
169 bb_perror_msg_and_die("setrlimit");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000170}
171
Denis Vlasenkob3571492008-07-30 21:23:26 +0000172int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
173int chpst_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000174{
Denis Vlasenkob3571492008-07-30 21:23:26 +0000175 struct bb_uidgid_t ugid;
Denis Vlasenkoec73d302008-08-05 17:43:29 +0000176 char *set_user = set_user; /* for compiler */
177 char *env_user = env_user;
178 char *env_dir = env_dir;
Denis Vlasenkob3571492008-07-30 21:23:26 +0000179 char *root;
180 char *nicestr;
181 unsigned limita;
182 unsigned limitc;
183 unsigned limitd;
184 unsigned limitf;
185 unsigned limitl;
186 unsigned limitm;
187 unsigned limito;
188 unsigned limitp;
189 unsigned limitr;
190 unsigned limits;
191 unsigned limitt;
192 unsigned opt;
193
194 if ((ENABLE_CHPST && applet_name[0] == 'c')
195 || (ENABLE_SOFTLIMIT && applet_name[1] == 'o')
196 ) {
197 // FIXME: can we live with int-sized limits?
198 // can we live with 40000 days?
199 // if yes -> getopt converts strings to numbers for us
200 opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+";
201 opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000202 IF_CHPST("/:n:vP012"),
Denis Vlasenkob3571492008-07-30 21:23:26 +0000203 &limita, &limitc, &limitd, &limitf, &limitl,
204 &limitm, &limito, &limitp, &limitr, &limits, &limitt,
205 &set_user, &env_user, &env_dir
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000206 IF_CHPST(, &root, &nicestr));
Denis Vlasenkob3571492008-07-30 21:23:26 +0000207 argv += optind;
208 if (opt & OPT_m) { // -m means -asld
209 limita = limits = limitl = limitd = limitm;
210 opt |= (OPT_s | OPT_l | OPT_a | OPT_d);
211 }
212 } else {
213 option_mask32 = opt = 0;
214 argv++;
Denis Vlasenkob9c262b2008-08-20 22:19:27 +0000215 if (!*argv)
216 bb_show_usage();
Denis Vlasenkob3571492008-07-30 21:23:26 +0000217 }
218
219 // envdir?
220 if (ENABLE_ENVDIR && applet_name[3] == 'd') {
221 env_dir = *argv++;
222 opt |= OPT_e;
223 }
224
225 // setuidgid?
Denis Vlasenkob9c262b2008-08-20 22:19:27 +0000226 if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
Denis Vlasenkob3571492008-07-30 21:23:26 +0000227 set_user = *argv++;
228 opt |= OPT_u;
229 }
230
231 // envuidgid?
Denis Vlasenko69ed3092008-08-15 21:03:17 +0000232 if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
Denis Vlasenkob3571492008-07-30 21:23:26 +0000233 env_user = *argv++;
234 opt |= OPT_U;
235 }
236
237 // we must have PROG [ARGS]
238 if (!*argv)
239 bb_show_usage();
240
241 // set limits
242 if (opt & OPT_d) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000243#ifdef RLIMIT_DATA
244 limit(RLIMIT_DATA, limitd);
245#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000246 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000247 bb_error_msg("system does not support RLIMIT_%s",
248 "DATA");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000249#endif
250 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000251 if (opt & OPT_s) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000252#ifdef RLIMIT_STACK
253 limit(RLIMIT_STACK, limits);
254#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000255 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000256 bb_error_msg("system does not support RLIMIT_%s",
257 "STACK");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000258#endif
259 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000260 if (opt & OPT_l) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000261#ifdef RLIMIT_MEMLOCK
262 limit(RLIMIT_MEMLOCK, limitl);
263#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000264 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000265 bb_error_msg("system does not support RLIMIT_%s",
266 "MEMLOCK");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000267#endif
268 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000269 if (opt & OPT_a) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000270#ifdef RLIMIT_VMEM
271 limit(RLIMIT_VMEM, limita);
272#else
273#ifdef RLIMIT_AS
274 limit(RLIMIT_AS, limita);
275#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000276 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000277 bb_error_msg("system does not support RLIMIT_%s",
278 "VMEM");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000279#endif
280#endif
281 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000282 if (opt & OPT_o) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000283#ifdef RLIMIT_NOFILE
284 limit(RLIMIT_NOFILE, limito);
285#else
286#ifdef RLIMIT_OFILE
287 limit(RLIMIT_OFILE, limito);
288#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000289 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000290 bb_error_msg("system does not support RLIMIT_%s",
291 "NOFILE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000292#endif
293#endif
294 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000295 if (opt & OPT_p) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000296#ifdef RLIMIT_NPROC
297 limit(RLIMIT_NPROC, limitp);
298#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000299 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000300 bb_error_msg("system does not support RLIMIT_%s",
301 "NPROC");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000302#endif
303 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000304 if (opt & OPT_f) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000305#ifdef RLIMIT_FSIZE
306 limit(RLIMIT_FSIZE, limitf);
307#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000308 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000309 bb_error_msg("system does not support RLIMIT_%s",
310 "FSIZE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000311#endif
312 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000313 if (opt & OPT_c) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000314#ifdef RLIMIT_CORE
315 limit(RLIMIT_CORE, limitc);
316#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000317 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000318 bb_error_msg("system does not support RLIMIT_%s",
319 "CORE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000320#endif
321 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000322 if (opt & OPT_r) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000323#ifdef RLIMIT_RSS
324 limit(RLIMIT_RSS, limitr);
325#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000326 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000327 bb_error_msg("system does not support RLIMIT_%s",
328 "RSS");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000329#endif
330 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000331 if (opt & OPT_t) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000332#ifdef RLIMIT_CPU
333 limit(RLIMIT_CPU, limitt);
334#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000335 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000336 bb_error_msg("system does not support RLIMIT_%s",
337 "CPU");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000338#endif
339 }
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000340
Denis Vlasenkob3571492008-07-30 21:23:26 +0000341 if (opt & OPT_P)
342 setsid();
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000343
Denis Vlasenkob3571492008-07-30 21:23:26 +0000344 if (opt & OPT_e)
345 edir(env_dir);
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000346
Denis Vlasenkob3571492008-07-30 21:23:26 +0000347 // FIXME: chrooted jail must have /etc/passwd if we move this after chroot!
348 // OTOH chroot fails for non-roots!
349 // SOLUTION: cache uid/gid before chroot, apply uid/gid after
350 if (opt & OPT_U) {
351 xget_uidgid(&ugid, env_user);
352 xsetenv("GID", utoa(ugid.gid));
353 xsetenv("UID", utoa(ugid.uid));
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000354 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000355
Denis Vlasenkob3571492008-07-30 21:23:26 +0000356 if (opt & OPT_u) {
357 xget_uidgid(&ugid, set_user);
358 }
359
360 if (opt & OPT_root) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000361 xchdir(root);
Denis Vlasenko394eebe2008-02-25 20:30:24 +0000362 xchroot(".");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000363 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000364
365 if (opt & OPT_u) {
366 if (setgroups(1, &ugid.gid) == -1)
367 bb_perror_msg_and_die("setgroups");
368 xsetgid(ugid.gid);
369 xsetuid(ugid.uid);
370 }
371
372 if (opt & OPT_n) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000373 errno = 0;
Denis Vlasenkob3571492008-07-30 21:23:26 +0000374 if (nice(xatoi(nicestr)) == -1)
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000375 bb_perror_msg_and_die("nice");
376 }
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000377
Denis Vlasenkob3571492008-07-30 21:23:26 +0000378 if (opt & OPT_0)
379 close(STDIN_FILENO);
380 if (opt & OPT_1)
381 close(STDOUT_FILENO);
382 if (opt & OPT_2)
383 close(STDERR_FILENO);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000384
Denis Vlasenko1d76f432007-02-06 01:20:12 +0000385 BB_EXECVP(argv[0], argv);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000386 bb_perror_msg_and_die("exec %s", argv[0]);
387}