blob: 082d7268215ed0d32a7ba77a252f03a346486d18 [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 (;;) {
Bernhard Reutner-Fischerca254492009-10-26 23:27:07 +0100107 RESERVE_CONFIG_BUFFER(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);
Bernhard Reutner-Fischerca254492009-10-26 23:27:07 +0100151 RELEASE_CONFIG_BUFFER(buf);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000152 }
153 closedir(dir);
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000154 if (fchdir(wdir) == -1)
155 bb_perror_msg_and_die("fchdir");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000156 close(wdir);
157}
158
159static void limit(int what, long l)
160{
161 struct rlimit r;
162
Denis Vlasenko4e6d5112008-03-12 22:14:34 +0000163 /* Never fails under Linux (except if you pass it bad arguments) */
164 getrlimit(what, &r);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000165 if ((l < 0) || (l > r.rlim_max))
166 r.rlim_cur = r.rlim_max;
167 else
168 r.rlim_cur = l;
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000169 if (setrlimit(what, &r) == -1)
170 bb_perror_msg_and_die("setrlimit");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000171}
172
Denis Vlasenkob3571492008-07-30 21:23:26 +0000173int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
174int chpst_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000175{
Denis Vlasenkob3571492008-07-30 21:23:26 +0000176 struct bb_uidgid_t ugid;
Denis Vlasenkoec73d302008-08-05 17:43:29 +0000177 char *set_user = set_user; /* for compiler */
178 char *env_user = env_user;
179 char *env_dir = env_dir;
Denis Vlasenkob3571492008-07-30 21:23:26 +0000180 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;
194
195 if ((ENABLE_CHPST && applet_name[0] == 'c')
196 || (ENABLE_SOFTLIMIT && applet_name[1] == 'o')
197 ) {
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:"
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000203 IF_CHPST("/:n:vP012"),
Denis Vlasenkob3571492008-07-30 21:23:26 +0000204 &limita, &limitc, &limitd, &limitf, &limitl,
205 &limitm, &limito, &limitp, &limitr, &limits, &limitt,
206 &set_user, &env_user, &env_dir
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000207 IF_CHPST(, &root, &nicestr));
Denis Vlasenkob3571492008-07-30 21:23:26 +0000208 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);
212 }
213 } else {
214 option_mask32 = opt = 0;
215 argv++;
Denis Vlasenkob9c262b2008-08-20 22:19:27 +0000216 if (!*argv)
217 bb_show_usage();
Denis Vlasenkob3571492008-07-30 21:23:26 +0000218 }
219
220 // envdir?
221 if (ENABLE_ENVDIR && applet_name[3] == 'd') {
222 env_dir = *argv++;
223 opt |= OPT_e;
224 }
225
226 // setuidgid?
Denis Vlasenkob9c262b2008-08-20 22:19:27 +0000227 if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
Denis Vlasenkob3571492008-07-30 21:23:26 +0000228 set_user = *argv++;
229 opt |= OPT_u;
230 }
231
232 // envuidgid?
Denis Vlasenko69ed3092008-08-15 21:03:17 +0000233 if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
Denis Vlasenkob3571492008-07-30 21:23:26 +0000234 env_user = *argv++;
235 opt |= OPT_U;
236 }
237
238 // we must have PROG [ARGS]
239 if (!*argv)
240 bb_show_usage();
241
242 // set limits
243 if (opt & OPT_d) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000244#ifdef RLIMIT_DATA
245 limit(RLIMIT_DATA, limitd);
246#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000247 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000248 bb_error_msg("system does not support RLIMIT_%s",
249 "DATA");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000250#endif
251 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000252 if (opt & OPT_s) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000253#ifdef RLIMIT_STACK
254 limit(RLIMIT_STACK, limits);
255#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000256 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000257 bb_error_msg("system does not support RLIMIT_%s",
258 "STACK");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000259#endif
260 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000261 if (opt & OPT_l) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000262#ifdef RLIMIT_MEMLOCK
263 limit(RLIMIT_MEMLOCK, limitl);
264#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000265 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000266 bb_error_msg("system does not support RLIMIT_%s",
267 "MEMLOCK");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000268#endif
269 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000270 if (opt & OPT_a) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000271#ifdef RLIMIT_VMEM
272 limit(RLIMIT_VMEM, limita);
273#else
274#ifdef RLIMIT_AS
275 limit(RLIMIT_AS, limita);
276#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000277 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000278 bb_error_msg("system does not support RLIMIT_%s",
279 "VMEM");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000280#endif
281#endif
282 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000283 if (opt & OPT_o) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000284#ifdef RLIMIT_NOFILE
285 limit(RLIMIT_NOFILE, limito);
286#else
287#ifdef RLIMIT_OFILE
288 limit(RLIMIT_OFILE, limito);
289#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000290 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000291 bb_error_msg("system does not support RLIMIT_%s",
292 "NOFILE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000293#endif
294#endif
295 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000296 if (opt & OPT_p) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000297#ifdef RLIMIT_NPROC
298 limit(RLIMIT_NPROC, limitp);
299#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000300 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000301 bb_error_msg("system does not support RLIMIT_%s",
302 "NPROC");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000303#endif
304 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000305 if (opt & OPT_f) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000306#ifdef RLIMIT_FSIZE
307 limit(RLIMIT_FSIZE, limitf);
308#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000309 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000310 bb_error_msg("system does not support RLIMIT_%s",
311 "FSIZE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000312#endif
313 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000314 if (opt & OPT_c) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000315#ifdef RLIMIT_CORE
316 limit(RLIMIT_CORE, limitc);
317#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000318 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000319 bb_error_msg("system does not support RLIMIT_%s",
320 "CORE");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000321#endif
322 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000323 if (opt & OPT_r) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000324#ifdef RLIMIT_RSS
325 limit(RLIMIT_RSS, limitr);
326#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000327 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000328 bb_error_msg("system does not support RLIMIT_%s",
329 "RSS");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000330#endif
331 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000332 if (opt & OPT_t) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000333#ifdef RLIMIT_CPU
334 limit(RLIMIT_CPU, limitt);
335#else
Denis Vlasenkob3571492008-07-30 21:23:26 +0000336 if (opt & OPT_v)
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000337 bb_error_msg("system does not support RLIMIT_%s",
338 "CPU");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000339#endif
340 }
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000341
Denis Vlasenkob3571492008-07-30 21:23:26 +0000342 if (opt & OPT_P)
343 setsid();
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000344
Denis Vlasenkob3571492008-07-30 21:23:26 +0000345 if (opt & OPT_e)
346 edir(env_dir);
Denis Vlasenko23e3e252007-10-05 21:23:49 +0000347
Denis Vlasenkob3571492008-07-30 21:23:26 +0000348 // 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));
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000355 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000356
Denis Vlasenkob3571492008-07-30 21:23:26 +0000357 if (opt & OPT_u) {
358 xget_uidgid(&ugid, set_user);
359 }
360
361 if (opt & OPT_root) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000362 xchdir(root);
Denis Vlasenko394eebe2008-02-25 20:30:24 +0000363 xchroot(".");
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000364 }
Denis Vlasenkob3571492008-07-30 21:23:26 +0000365
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);
371 }
372
373 if (opt & OPT_n) {
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000374 errno = 0;
Denis Vlasenkob3571492008-07-30 21:23:26 +0000375 if (nice(xatoi(nicestr)) == -1)
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000376 bb_perror_msg_and_die("nice");
377 }
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000378
Denis Vlasenkob3571492008-07-30 21:23:26 +0000379 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);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000385
Denis Vlasenko1d76f432007-02-06 01:20:12 +0000386 BB_EXECVP(argv[0], argv);
Denis Vlasenkof0a97fb2006-10-03 17:52:24 +0000387 bb_perror_msg_and_die("exec %s", argv[0]);
388}