source: branches/3.0/mindi-busybox/runit/chpst.c @ 3085

Last change on this file since 3085 was 2725, checked in by bruno, 8 years ago
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
  • Property svn:eol-style set to native
File size: 9.5 KB
Line 
1/*
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
28/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29/* Dependencies on runit_lib.c removed */
30
31#include "libbb.h"
32
33/*
34Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
35
36Only 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,
92};
93
94/* TODO: use recursive_action? */
95static NOINLINE void edir(const char *directory_name)
96{
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);
104    dir = xopendir(".");
105    for (;;) {
106        char buf[256];
107        char *tail;
108        int size;
109
110        errno = 0;
111        d = readdir(dir);
112        if (!d) {
113            if (errno)
114                bb_perror_msg_and_die("readdir %s",
115                        directory_name);
116            break;
117        }
118        if (d->d_name[0] == '.')
119            continue;
120        fd = open(d->d_name, O_RDONLY | O_NDELAY);
121        if (fd < 0) {
122            if ((errno == EISDIR) && directory_name) {
123                if (option_mask32 & OPT_v)
124                    bb_perror_msg("warning: %s/%s is a directory",
125                        directory_name, d->d_name);
126                continue;
127            }
128            bb_perror_msg_and_die("open %s/%s",
129                        directory_name, d->d_name);
130        }
131        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;
139        }
140        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);
150    }
151    closedir(dir);
152    if (fchdir(wdir) == -1)
153        bb_perror_msg_and_die("fchdir");
154    close(wdir);
155}
156
157static void limit(int what, long l)
158{
159    struct rlimit r;
160
161    /* Never fails under Linux (except if you pass it bad arguments) */
162    getrlimit(what, &r);
163    if ((l < 0) || (l > r.rlim_max))
164        r.rlim_cur = r.rlim_max;
165    else
166        r.rlim_cur = l;
167    if (setrlimit(what, &r) == -1)
168        bb_perror_msg_and_die("setrlimit");
169}
170
171int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
172int chpst_main(int argc UNUSED_PARAM, char **argv)
173{
174    struct bb_uidgid_t ugid;
175    char *set_user = set_user; /* for compiler */
176    char *env_user = env_user;
177    char *env_dir = env_dir;
178    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:"
201            IF_CHPST("/:n:vP012"),
202            &limita, &limitc, &limitd, &limitf, &limitl,
203            &limitm, &limito, &limitp, &limitr, &limits, &limitt,
204            &set_user, &env_user, &env_dir
205            IF_CHPST(, &root, &nicestr));
206        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++;
214        if (!*argv)
215            bb_show_usage();
216    }
217
218    // envdir?
219    if (ENABLE_ENVDIR && applet_name[3] == 'd') {
220        env_dir = *argv++;
221        opt |= OPT_e;
222    }
223
224    // setuidgid?
225    if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
226        set_user = *argv++;
227        opt |= OPT_u;
228    }
229
230    // envuidgid?
231    if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
232        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) {
242#ifdef RLIMIT_DATA
243        limit(RLIMIT_DATA, limitd);
244#else
245        if (opt & OPT_v)
246            bb_error_msg("system does not support RLIMIT_%s",
247                "DATA");
248#endif
249    }
250    if (opt & OPT_s) {
251#ifdef RLIMIT_STACK
252        limit(RLIMIT_STACK, limits);
253#else
254        if (opt & OPT_v)
255            bb_error_msg("system does not support RLIMIT_%s",
256                "STACK");
257#endif
258    }
259    if (opt & OPT_l) {
260#ifdef RLIMIT_MEMLOCK
261        limit(RLIMIT_MEMLOCK, limitl);
262#else
263        if (opt & OPT_v)
264            bb_error_msg("system does not support RLIMIT_%s",
265                "MEMLOCK");
266#endif
267    }
268    if (opt & OPT_a) {
269#ifdef RLIMIT_VMEM
270        limit(RLIMIT_VMEM, limita);
271#else
272#ifdef RLIMIT_AS
273        limit(RLIMIT_AS, limita);
274#else
275        if (opt & OPT_v)
276            bb_error_msg("system does not support RLIMIT_%s",
277                "VMEM");
278#endif
279#endif
280    }
281    if (opt & OPT_o) {
282#ifdef RLIMIT_NOFILE
283        limit(RLIMIT_NOFILE, limito);
284#else
285#ifdef RLIMIT_OFILE
286        limit(RLIMIT_OFILE, limito);
287#else
288        if (opt & OPT_v)
289            bb_error_msg("system does not support RLIMIT_%s",
290                "NOFILE");
291#endif
292#endif
293    }
294    if (opt & OPT_p) {
295#ifdef RLIMIT_NPROC
296        limit(RLIMIT_NPROC, limitp);
297#else
298        if (opt & OPT_v)
299            bb_error_msg("system does not support RLIMIT_%s",
300                "NPROC");
301#endif
302    }
303    if (opt & OPT_f) {
304#ifdef RLIMIT_FSIZE
305        limit(RLIMIT_FSIZE, limitf);
306#else
307        if (opt & OPT_v)
308            bb_error_msg("system does not support RLIMIT_%s",
309                "FSIZE");
310#endif
311    }
312    if (opt & OPT_c) {
313#ifdef RLIMIT_CORE
314        limit(RLIMIT_CORE, limitc);
315#else
316        if (opt & OPT_v)
317            bb_error_msg("system does not support RLIMIT_%s",
318                "CORE");
319#endif
320    }
321    if (opt & OPT_r) {
322#ifdef RLIMIT_RSS
323        limit(RLIMIT_RSS, limitr);
324#else
325        if (opt & OPT_v)
326            bb_error_msg("system does not support RLIMIT_%s",
327                "RSS");
328#endif
329    }
330    if (opt & OPT_t) {
331#ifdef RLIMIT_CPU
332        limit(RLIMIT_CPU, limitt);
333#else
334        if (opt & OPT_v)
335            bb_error_msg("system does not support RLIMIT_%s",
336                "CPU");
337#endif
338    }
339
340    if (opt & OPT_P)
341        setsid();
342
343    if (opt & OPT_e)
344        edir(env_dir);
345
346    // 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));
353    }
354
355    if (opt & OPT_u) {
356        xget_uidgid(&ugid, set_user);
357    }
358
359    if (opt & OPT_root) {
360        xchdir(root);
361        xchroot(".");
362    }
363
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) {
372        errno = 0;
373        if (nice(xatoi(nicestr)) == -1)
374            bb_perror_msg_and_die("nice");
375    }
376
377    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);
383
384    BB_EXECVP_or_die(argv);
385}
Note: See TracBrowser for help on using the repository browser.