busybox/runit/chpst.c
<<
>>
Prefs
   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}
 446