source: branches/3.2/mindi-busybox/runit/chpst.c @ 3232

Last change on this file since 3232 was 3232, checked in by bruno, 5 years ago
  • Update mindi-busybox to 1.21.1
  • Property svn:eol-style set to native
File size: 12.4 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//usage:#define chpst_trivial_usage
32//usage:       "[-vP012] [-u USER[:GRP]] [-U USER[:GRP]] [-e DIR]\n"
33//usage:       "    [-/ DIR] [-n NICE] [-m BYTES] [-d BYTES] [-o N]\n"
34//usage:       "    [-p N] [-f BYTES] [-c BYTES] PROG ARGS"
35//usage:#define chpst_full_usage "\n\n"
36//usage:       "Change the process state, run PROG\n"
37//usage:     "\n    -u USER[:GRP]   Set uid and gid"
38//usage:     "\n    -U USER[:GRP]   Set $UID and $GID in environment"
39//usage:     "\n    -e DIR      Set environment variables as specified by files"
40//usage:     "\n            in DIR: file=1st_line_of_file"
41//usage:     "\n    -/ DIR      Chroot to DIR"
42//usage:     "\n    -n NICE     Add NICE to nice value"
43//usage:     "\n    -m BYTES    Same as -d BYTES -s BYTES -l BYTES"
44//usage:     "\n    -d BYTES    Limit data segment"
45//usage:     "\n    -o N        Limit number of open files per process"
46//usage:     "\n    -p N        Limit number of processes per uid"
47//usage:     "\n    -f BYTES    Limit output file sizes"
48//usage:     "\n    -c BYTES    Limit core file size"
49//usage:     "\n    -v      Verbose"
50//usage:     "\n    -P      Create new process group"
51//usage:     "\n    -0      Close stdin"
52//usage:     "\n    -1      Close stdout"
53//usage:     "\n    -2      Close stderr"
54//usage:
55//usage:#define envdir_trivial_usage
56//usage:       "DIR PROG ARGS"
57//usage:#define envdir_full_usage "\n\n"
58//usage:       "Set various environment variables as specified by files\n"
59//usage:       "in the directory DIR, run PROG"
60//usage:
61//usage:#define envuidgid_trivial_usage
62//usage:       "USER PROG ARGS"
63//usage:#define envuidgid_full_usage "\n\n"
64//usage:       "Set $UID to USER's uid and $GID to USER's gid, run PROG"
65//usage:
66//usage:#define setuidgid_trivial_usage
67//usage:       "USER PROG ARGS"
68//usage:#define setuidgid_full_usage "\n\n"
69//usage:       "Set uid and gid to USER's uid and gid, drop supplementary group ids,\n"
70//usage:       "run PROG"
71//usage:
72//usage:#define softlimit_trivial_usage
73//usage:       "[-a BYTES] [-m BYTES] [-d BYTES] [-s BYTES] [-l BYTES]\n"
74//usage:       "    [-f BYTES] [-c BYTES] [-r BYTES] [-o N] [-p N] [-t N]\n"
75//usage:       "    PROG ARGS"
76//usage:#define softlimit_full_usage "\n\n"
77//usage:       "Set soft resource limits, then run PROG\n"
78//usage:     "\n    -a BYTES    Limit total size of all segments"
79//usage:     "\n    -m BYTES    Same as -d BYTES -s BYTES -l BYTES -a BYTES"
80//usage:     "\n    -d BYTES    Limit data segment"
81//usage:     "\n    -s BYTES    Limit stack segment"
82//usage:     "\n    -l BYTES    Limit locked memory size"
83//usage:     "\n    -o N        Limit number of open files per process"
84//usage:     "\n    -p N        Limit number of processes per uid"
85//usage:     "\nOptions controlling file sizes:"
86//usage:     "\n    -f BYTES    Limit output file sizes"
87//usage:     "\n    -c BYTES    Limit core file size"
88//usage:     "\nEfficiency opts:"
89//usage:     "\n    -r BYTES    Limit resident set size"
90//usage:     "\n    -t N        Limit CPU time, process receives"
91//usage:     "\n            a SIGXCPU after N seconds"
92
93#include "libbb.h"
94#include <sys/resource.h> /* getrlimit */
95
96/*
97Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit.
98
99Only softlimit and chpst are taking options:
100
101# common
102-o N            Limit number of open files per process
103-p N            Limit number of processes per uid
104-m BYTES        Same as -d BYTES -s BYTES -l BYTES [-a BYTES]
105-d BYTES        Limit data segment
106-f BYTES        Limit output file sizes
107-c BYTES        Limit core file size
108# softlimit
109-a BYTES        Limit total size of all segments
110-s BYTES        Limit stack segment
111-l BYTES        Limit locked memory size
112-r BYTES        Limit resident set size
113-t N            Limit CPU time
114# chpst
115-u USER[:GRP]   Set uid and gid
116-U USER[:GRP]   Set $UID and $GID in environment
117-e DIR          Set environment variables as specified by files in DIR
118-/ DIR          Chroot to DIR
119-n NICE         Add NICE to nice value
120-v              Verbose
121-P              Create new process group
122-0 -1 -2        Close fd 0,1,2
123
124Even though we accept all these options for both softlimit and chpst,
125they are not to be advertised on their help texts.
126We have enough problems with feature creep in other people's
127software, don't want to add our own.
128
129envdir, envuidgid, setuidgid take no options, but they reuse code which
130handles -e, -U and -u.
131*/
132
133enum {
134    OPT_a = (1 << 0) * ENABLE_SOFTLIMIT,
135    OPT_c = (1 << 1) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
136    OPT_d = (1 << 2) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
137    OPT_f = (1 << 3) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
138    OPT_l = (1 << 4) * ENABLE_SOFTLIMIT,
139    OPT_m = (1 << 5) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
140    OPT_o = (1 << 6) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
141    OPT_p = (1 << 7) * (ENABLE_SOFTLIMIT || ENABLE_CHPST),
142    OPT_r = (1 << 8) * ENABLE_SOFTLIMIT,
143    OPT_s = (1 << 9) * ENABLE_SOFTLIMIT,
144    OPT_t = (1 << 10) * ENABLE_SOFTLIMIT,
145    OPT_u = (1 << 11) * (ENABLE_CHPST || ENABLE_SETUIDGID),
146    OPT_U = (1 << 12) * (ENABLE_CHPST || ENABLE_ENVUIDGID),
147    OPT_e = (1 << 13) * (ENABLE_CHPST || ENABLE_ENVDIR),
148    OPT_root = (1 << 14) * ENABLE_CHPST,
149    OPT_n = (1 << 15) * ENABLE_CHPST,
150    OPT_v = (1 << 16) * ENABLE_CHPST,
151    OPT_P = (1 << 17) * ENABLE_CHPST,
152    OPT_0 = (1 << 18) * ENABLE_CHPST,
153    OPT_1 = (1 << 19) * ENABLE_CHPST,
154    OPT_2 = (1 << 20) * ENABLE_CHPST,
155};
156
157/* TODO: use recursive_action? */
158static NOINLINE void edir(const char *directory_name)
159{
160    int wdir;
161    DIR *dir;
162    struct dirent *d;
163    int fd;
164
165    wdir = xopen(".", O_RDONLY | O_NDELAY);
166    xchdir(directory_name);
167    dir = xopendir(".");
168    for (;;) {
169        char buf[256];
170        char *tail;
171        int size;
172
173        errno = 0;
174        d = readdir(dir);
175        if (!d) {
176            if (errno)
177                bb_perror_msg_and_die("readdir %s",
178                        directory_name);
179            break;
180        }
181        if (d->d_name[0] == '.')
182            continue;
183        fd = open(d->d_name, O_RDONLY | O_NDELAY);
184        if (fd < 0) {
185            if ((errno == EISDIR) && directory_name) {
186                if (option_mask32 & OPT_v)
187                    bb_perror_msg("warning: %s/%s is a directory",
188                        directory_name, d->d_name);
189                continue;
190            }
191            bb_perror_msg_and_die("open %s/%s",
192                        directory_name, d->d_name);
193        }
194        size = full_read(fd, buf, sizeof(buf)-1);
195        close(fd);
196        if (size < 0)
197            bb_perror_msg_and_die("read %s/%s",
198                    directory_name, d->d_name);
199        if (size == 0) {
200            unsetenv(d->d_name);
201            continue;
202        }
203        buf[size] = '\n';
204        tail = strchr(buf, '\n');
205        /* skip trailing whitespace */
206        while (1) {
207            *tail = '\0';
208            tail--;
209            if (tail < buf || !isspace(*tail))
210                break;
211        }
212        xsetenv(d->d_name, buf);
213    }
214    closedir(dir);
215    if (fchdir(wdir) == -1)
216        bb_perror_msg_and_die("fchdir");
217    close(wdir);
218}
219
220static void limit(int what, long l)
221{
222    struct rlimit r;
223
224    /* Never fails under Linux (except if you pass it bad arguments) */
225    getrlimit(what, &r);
226    if ((l < 0) || (l > r.rlim_max))
227        r.rlim_cur = r.rlim_max;
228    else
229        r.rlim_cur = l;
230    if (setrlimit(what, &r) == -1)
231        bb_perror_msg_and_die("setrlimit");
232}
233
234int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
235int chpst_main(int argc UNUSED_PARAM, char **argv)
236{
237    struct bb_uidgid_t ugid;
238    char *set_user = set_user; /* for compiler */
239    char *env_user = env_user;
240    char *env_dir = env_dir;
241    char *root;
242    char *nicestr;
243    unsigned limita;
244    unsigned limitc;
245    unsigned limitd;
246    unsigned limitf;
247    unsigned limitl;
248    unsigned limitm;
249    unsigned limito;
250    unsigned limitp;
251    unsigned limitr;
252    unsigned limits;
253    unsigned limitt;
254    unsigned opt;
255
256    if ((ENABLE_CHPST && applet_name[0] == 'c')
257     || (ENABLE_SOFTLIMIT && applet_name[1] == 'o')
258    ) {
259        // FIXME: can we live with int-sized limits?
260        // can we live with 40000 days?
261        // if yes -> getopt converts strings to numbers for us
262        opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+";
263        opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:"
264            IF_CHPST("/:n:vP012"),
265            &limita, &limitc, &limitd, &limitf, &limitl,
266            &limitm, &limito, &limitp, &limitr, &limits, &limitt,
267            &set_user, &env_user, &env_dir
268            IF_CHPST(, &root, &nicestr));
269        argv += optind;
270        if (opt & OPT_m) { // -m means -asld
271            limita = limits = limitl = limitd = limitm;
272            opt |= (OPT_s | OPT_l | OPT_a | OPT_d);
273        }
274    } else {
275        option_mask32 = opt = 0;
276        argv++;
277        if (!*argv)
278            bb_show_usage();
279    }
280
281    // envdir?
282    if (ENABLE_ENVDIR && applet_name[3] == 'd') {
283        env_dir = *argv++;
284        opt |= OPT_e;
285    }
286
287    // setuidgid?
288    if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
289        set_user = *argv++;
290        opt |= OPT_u;
291    }
292
293    // envuidgid?
294    if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
295        env_user = *argv++;
296        opt |= OPT_U;
297    }
298
299    // we must have PROG [ARGS]
300    if (!*argv)
301        bb_show_usage();
302
303    // set limits
304    if (opt & OPT_d) {
305#ifdef RLIMIT_DATA
306        limit(RLIMIT_DATA, limitd);
307#else
308        if (opt & OPT_v)
309            bb_error_msg("system does not support RLIMIT_%s",
310                "DATA");
311#endif
312    }
313    if (opt & OPT_s) {
314#ifdef RLIMIT_STACK
315        limit(RLIMIT_STACK, limits);
316#else
317        if (opt & OPT_v)
318            bb_error_msg("system does not support RLIMIT_%s",
319                "STACK");
320#endif
321    }
322    if (opt & OPT_l) {
323#ifdef RLIMIT_MEMLOCK
324        limit(RLIMIT_MEMLOCK, limitl);
325#else
326        if (opt & OPT_v)
327            bb_error_msg("system does not support RLIMIT_%s",
328                "MEMLOCK");
329#endif
330    }
331    if (opt & OPT_a) {
332#ifdef RLIMIT_VMEM
333        limit(RLIMIT_VMEM, limita);
334#else
335#ifdef RLIMIT_AS
336        limit(RLIMIT_AS, limita);
337#else
338        if (opt & OPT_v)
339            bb_error_msg("system does not support RLIMIT_%s",
340                "VMEM");
341#endif
342#endif
343    }
344    if (opt & OPT_o) {
345#ifdef RLIMIT_NOFILE
346        limit(RLIMIT_NOFILE, limito);
347#else
348#ifdef RLIMIT_OFILE
349        limit(RLIMIT_OFILE, limito);
350#else
351        if (opt & OPT_v)
352            bb_error_msg("system does not support RLIMIT_%s",
353                "NOFILE");
354#endif
355#endif
356    }
357    if (opt & OPT_p) {
358#ifdef RLIMIT_NPROC
359        limit(RLIMIT_NPROC, limitp);
360#else
361        if (opt & OPT_v)
362            bb_error_msg("system does not support RLIMIT_%s",
363                "NPROC");
364#endif
365    }
366    if (opt & OPT_f) {
367#ifdef RLIMIT_FSIZE
368        limit(RLIMIT_FSIZE, limitf);
369#else
370        if (opt & OPT_v)
371            bb_error_msg("system does not support RLIMIT_%s",
372                "FSIZE");
373#endif
374    }
375    if (opt & OPT_c) {
376#ifdef RLIMIT_CORE
377        limit(RLIMIT_CORE, limitc);
378#else
379        if (opt & OPT_v)
380            bb_error_msg("system does not support RLIMIT_%s",
381                "CORE");
382#endif
383    }
384    if (opt & OPT_r) {
385#ifdef RLIMIT_RSS
386        limit(RLIMIT_RSS, limitr);
387#else
388        if (opt & OPT_v)
389            bb_error_msg("system does not support RLIMIT_%s",
390                "RSS");
391#endif
392    }
393    if (opt & OPT_t) {
394#ifdef RLIMIT_CPU
395        limit(RLIMIT_CPU, limitt);
396#else
397        if (opt & OPT_v)
398            bb_error_msg("system does not support RLIMIT_%s",
399                "CPU");
400#endif
401    }
402
403    if (opt & OPT_P)
404        setsid();
405
406    if (opt & OPT_e)
407        edir(env_dir);
408
409    if (opt & (OPT_u|OPT_U))
410        xget_uidgid(&ugid, set_user);
411
412    // chrooted jail must have /etc/passwd if we move this after chroot.
413    // OTOH chroot fails for non-roots.
414    // Solution: cache uid/gid before chroot, apply uid/gid after.
415    if (opt & OPT_U) {
416        xsetenv("GID", utoa(ugid.gid));
417        xsetenv("UID", utoa(ugid.uid));
418    }
419
420    if (opt & OPT_root) {
421        xchroot(root);
422    }
423
424    if (opt & OPT_u) {
425        if (setgroups(1, &ugid.gid) == -1)
426            bb_perror_msg_and_die("setgroups");
427        xsetgid(ugid.gid);
428        xsetuid(ugid.uid);
429    }
430
431    if (opt & OPT_n) {
432        errno = 0;
433        if (nice(xatoi(nicestr)) == -1)
434            bb_perror_msg_and_die("nice");
435    }
436
437    if (opt & OPT_0)
438        close(STDIN_FILENO);
439    if (opt & OPT_1)
440        close(STDOUT_FILENO);
441    if (opt & OPT_2)
442        close(STDERR_FILENO);
443
444    BB_EXECVP_or_die(argv);
445}
Note: See TracBrowser for help on using the repository browser.