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_dir = env_dir;
 240        char *root;
 241        char *nicestr;
 242        unsigned limita;
 243        unsigned limitc;
 244        unsigned limitd;
 245        unsigned limitf;
 246        unsigned limitl;
 247        unsigned limitm;
 248        unsigned limito;
 249        unsigned limitp;
 250        unsigned limitr;
 251        unsigned limits;
 252        unsigned limitt;
 253        unsigned opt;
 254
 255        if ((ENABLE_CHPST && applet_name[0] == 'c')
 256         || (ENABLE_SOFTLIMIT && applet_name[1] == 'o')
 257        ) {
 258                // FIXME: can we live with int-sized limits?
 259                // can we live with 40000 days?
 260                // if yes -> getopt converts strings to numbers for us
 261                opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+";
 262                opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:"
 263                        IF_CHPST("/:n:vP012"),
 264                        &limita, &limitc, &limitd, &limitf, &limitl,
 265                        &limitm, &limito, &limitp, &limitr, &limits, &limitt,
 266                        &set_user, &set_user, &env_dir
 267                        IF_CHPST(, &root, &nicestr));
 268                argv += optind;
 269                if (opt & OPT_m) { // -m means -asld
 270                        limita = limits = limitl = limitd = limitm;
 271                        opt |= (OPT_s | OPT_l | OPT_a | OPT_d);
 272                }
 273        } else {
 274                option_mask32 = opt = 0;
 275                argv++;
 276                if (!*argv)
 277                        bb_show_usage();
 278        }
 279
 280        // envdir?
 281        if (ENABLE_ENVDIR && applet_name[3] == 'd') {
 282                env_dir = *argv++;
 283                opt |= OPT_e;
 284        }
 285
 286        // setuidgid?
 287        if (ENABLE_SETUIDGID && applet_name[1] == 'e') {
 288                set_user = *argv++;
 289                opt |= OPT_u;
 290        }
 291
 292        // envuidgid?
 293        if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') {
 294                set_user = *argv++;
 295                opt |= OPT_U;
 296        }
 297
 298        // we must have PROG [ARGS]
 299        if (!*argv)
 300                bb_show_usage();
 301
 302        // set limits
 303        if (opt & OPT_d) {
 304#ifdef RLIMIT_DATA
 305                limit(RLIMIT_DATA, limitd);
 306#else
 307                if (opt & OPT_v)
 308                        bb_error_msg("system does not support RLIMIT_%s",
 309                                "DATA");
 310#endif
 311        }
 312        if (opt & OPT_s) {
 313#ifdef RLIMIT_STACK
 314                limit(RLIMIT_STACK, limits);
 315#else
 316                if (opt & OPT_v)
 317                        bb_error_msg("system does not support RLIMIT_%s",
 318                                "STACK");
 319#endif
 320        }
 321        if (opt & OPT_l) {
 322#ifdef RLIMIT_MEMLOCK
 323                limit(RLIMIT_MEMLOCK, limitl);
 324#else
 325                if (opt & OPT_v)
 326                        bb_error_msg("system does not support RLIMIT_%s",
 327                                "MEMLOCK");
 328#endif
 329        }
 330        if (opt & OPT_a) {
 331#ifdef RLIMIT_VMEM
 332                limit(RLIMIT_VMEM, limita);
 333#else
 334#ifdef RLIMIT_AS
 335                limit(RLIMIT_AS, limita);
 336#else
 337                if (opt & OPT_v)
 338                        bb_error_msg("system does not support RLIMIT_%s",
 339                                "VMEM");
 340#endif
 341#endif
 342        }
 343        if (opt & OPT_o) {
 344#ifdef RLIMIT_NOFILE
 345                limit(RLIMIT_NOFILE, limito);
 346#else
 347#ifdef RLIMIT_OFILE
 348                limit(RLIMIT_OFILE, limito);
 349#else
 350                if (opt & OPT_v)
 351                        bb_error_msg("system does not support RLIMIT_%s",
 352                                "NOFILE");
 353#endif
 354#endif
 355        }
 356        if (opt & OPT_p) {
 357#ifdef RLIMIT_NPROC
 358                limit(RLIMIT_NPROC, limitp);
 359#else
 360                if (opt & OPT_v)
 361                        bb_error_msg("system does not support RLIMIT_%s",
 362                                "NPROC");
 363#endif
 364        }
 365        if (opt & OPT_f) {
 366#ifdef RLIMIT_FSIZE
 367                limit(RLIMIT_FSIZE, limitf);
 368#else
 369                if (opt & OPT_v)
 370                        bb_error_msg("system does not support RLIMIT_%s",
 371                                "FSIZE");
 372#endif
 373        }
 374        if (opt & OPT_c) {
 375#ifdef RLIMIT_CORE
 376                limit(RLIMIT_CORE, limitc);
 377#else
 378                if (opt & OPT_v)
 379                        bb_error_msg("system does not support RLIMIT_%s",
 380                                "CORE");
 381#endif
 382        }
 383        if (opt & OPT_r) {
 384#ifdef RLIMIT_RSS
 385                limit(RLIMIT_RSS, limitr);
 386#else
 387                if (opt & OPT_v)
 388                        bb_error_msg("system does not support RLIMIT_%s",
 389                                "RSS");
 390#endif
 391        }
 392        if (opt & OPT_t) {
 393#ifdef RLIMIT_CPU
 394                limit(RLIMIT_CPU, limitt);
 395#else
 396                if (opt & OPT_v)
 397                        bb_error_msg("system does not support RLIMIT_%s",
 398                                "CPU");
 399#endif
 400        }
 401
 402        if (opt & OPT_P)
 403                setsid();
 404
 405        if (opt & OPT_e)
 406                edir(env_dir);
 407
 408        if (opt & (OPT_u|OPT_U))
 409                xget_uidgid(&ugid, set_user);
 410
 411        // chrooted jail must have /etc/passwd if we move this after chroot.
 412        // OTOH chroot fails for non-roots.
 413        // Solution: cache uid/gid before chroot, apply uid/gid after.
 414        if (opt & OPT_U) {
 415                xsetenv("GID", utoa(ugid.gid));
 416                xsetenv("UID", utoa(ugid.uid));
 417        }
 418
 419        if (opt & OPT_root) {
 420                xchroot(root);
 421        }
 422
 423        if (opt & OPT_u) {
 424                if (setgroups(1, &ugid.gid) == -1)
 425                        bb_perror_msg_and_die("setgroups");
 426                xsetgid(ugid.gid);
 427                xsetuid(ugid.uid);
 428        }
 429
 430        if (opt & OPT_n) {
 431                errno = 0;
 432                if (nice(xatoi(nicestr)) == -1)
 433                        bb_perror_msg_and_die("nice");
 434        }
 435
 436        if (opt & OPT_0)
 437                close(STDIN_FILENO);
 438        if (opt & OPT_1)
 439                close(STDOUT_FILENO);
 440        if (opt & OPT_2)
 441                close(STDERR_FILENO);
 442
 443        BB_EXECVP_or_die(argv);
 444}
 445