runit

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

chpst.c (14204B)


      1 #include <sys/types.h>
      2 #include <time.h>
      3 #include <sys/time.h>
      4 #include <sys/resource.h>
      5 #include <unistd.h>
      6 #include <grp.h>
      7 #include <sys/types.h>
      8 #include <dirent.h>
      9 #include "sgetopt.h"
     10 #include "error.h"
     11 #include "strerr.h"
     12 #include "str.h"
     13 #include "uidgid.h"
     14 #include "strerr.h"
     15 #include "scan.h"
     16 #include "fmt.h"
     17 #include "lock.h"
     18 #include "pathexec.h"
     19 #include "stralloc.h"
     20 #include "byte.h"
     21 #include "open.h"
     22 #include "openreadclose.h"
     23 
     24 #define USAGE_MAIN " [-vP012] [-u user[:group]] [-U user[:group]] [-b argv0] [-e dir] [-/ root] [-C pwd] [-n nice] [-l|-L lock] [-m n] [-d n] [-o n] [-p n] [-f n] [-c n] prog"
     25 #define FATAL "chpst: fatal: "
     26 #define WARNING "chpst: warning: "
     27 
     28 static const char *progname;
     29 static stralloc sa;
     30 
     31 static void fatal(const char *m) { strerr_die3sys(111, FATAL, m, ": "); }
     32 static void fatal2(const char *m0, const char *m1)
     33 {
     34   strerr_die5sys(111, FATAL, m0, ": ", m1, ": ");
     35 }
     36 static void fatalx(const char *m0, const char *m1)
     37 {
     38   strerr_die4x(111, FATAL, m0, ": ", m1);
     39 }
     40 static void warn(const char *m) { strerr_warn2(WARNING, m, 0); }
     41 static void die_nomem( void ) { strerr_die2x(111, FATAL, "out of memory."); }
     42 static void usage( void ) { strerr_die4x(100, "usage: ", progname, USAGE_MAIN, "\n"); }
     43 
     44 static char *set_user =0;
     45 static char *env_user =0;
     46 static const char *argv0 =0;
     47 static const char *env_dir =0;
     48 static unsigned int verbose =0;
     49 static unsigned int pgrp =0;
     50 static unsigned int nostdin =0;
     51 static unsigned int nostdout =0;
     52 static unsigned int nostderr =0;
     53 static long limitd =-2;
     54 static long limits =-2;
     55 static long limitl =-2;
     56 static long limita =-2;
     57 static long limito =-2;
     58 static long limitp =-2;
     59 static long limitf =-2;
     60 static long limitc =-2;
     61 static long limitr =-2;
     62 static long limitt =-2;
     63 static long nicelvl =0;
     64 static const char *lock =0;
     65 static const char *root =0;
     66 static const char *pwd =0;
     67 static unsigned int lockdelay;
     68 
     69 static void suidgid(char *user, unsigned int ext)
     70 {
     71   struct uidgid ugid;
     72 
     73   if (ext) {
     74     if (! uidgids_get(&ugid, user)) {
     75       if (*user == ':') fatalx("invalid uid/gids", user +1);
     76       if (errno) fatal("unable to get password/group file entry");
     77       fatalx("unknown user/group", user);
     78     }
     79   }
     80   else
     81     if (! uidgid_get(&ugid, user)) {
     82       if (errno) fatal("unable to get password file entry");
     83       fatalx("unknown account", user);
     84     }
     85   if (setgroups(ugid.gids, ugid.gid) == -1) fatal("unable to setgroups");
     86   if (setgid(*ugid.gid) == -1) fatal("unable to setgid");
     87   if (setuid(ugid.uid) == -1) fatal("unable to setuid");
     88 }
     89 
     90 static void euidgid(char *user, unsigned int ext)
     91 {
     92   struct uidgid ugid;
     93   char bufnum[FMT_ULONG];
     94 
     95   if (ext) {
     96     if (! uidgids_get(&ugid, user)) {
     97       if (*user == ':') fatalx("invalid uid/gids", user +1);
     98       if (errno) fatal("unable to get password/group file entry");
     99       fatalx("unknown user/group", user);
    100     }
    101   }
    102   else
    103     if (! uidgid_get(&ugid, user)) {
    104       if (errno) fatal("unable to get password file entry");
    105       fatalx("unknown account", user);
    106     }
    107   bufnum[fmt_ulong(bufnum, *ugid.gid)] =0;
    108   if (! pathexec_env("GID", bufnum)) die_nomem();
    109   bufnum[fmt_ulong(bufnum, ugid.uid)] =0;
    110   if (! pathexec_env("UID", bufnum)) die_nomem();
    111 }
    112 
    113 static void edir(const char *dirname)
    114 {
    115   int wdir;
    116   DIR *dir;
    117   struct dirent *d;
    118   int i;
    119 
    120   if ((wdir =open_read(".")) == -1)
    121     fatal("unable to open current working directory");
    122   if (chdir(dirname)) fatal2("unable to switch to directory", dirname);
    123   if (! (dir =opendir("."))) fatal2("unable to open directory", dirname);
    124   for (;;) {
    125     errno =0;
    126     d =readdir(dir);
    127     if (! d) {
    128       if (errno) fatal2("unable to read directory", dirname);
    129       break;
    130     }
    131     if (d->d_name[0] == '.') continue;
    132     if (openreadclose(d->d_name, &sa, 256) == -1) {
    133       if ((errno == error_isdir) && env_dir) {
    134         if (verbose)
    135           strerr_warn6(WARNING, "unable to read ", dirname, "/",
    136                        d->d_name, ": ", &strerr_sys);
    137         continue;
    138       }
    139       else
    140         strerr_die6sys(111, FATAL, "unable to read ", dirname, "/",
    141                              d->d_name, ": ");
    142     }
    143     if (sa.len) {
    144       sa.len =byte_chr(sa.s, sa.len, '\n');
    145       while (sa.len && (sa.s[sa.len -1] == ' ' || sa.s[sa.len -1] == '\t'))
    146         --sa.len;
    147       for (i =0; i < sa.len; ++i) if (! sa.s[i]) sa.s[i] ='\n';
    148       if (! stralloc_0(&sa)) die_nomem();
    149       if (! pathexec_env(d->d_name, sa.s)) die_nomem();
    150     }
    151     else
    152       if (! pathexec_env(d->d_name, 0)) die_nomem();
    153   }
    154   closedir(dir);
    155   if (fchdir(wdir) == -1) fatal("unable to switch to starting directory");
    156   close(wdir);
    157 }
    158 
    159 static void slock_die(const char *m, const char *f, unsigned int x)
    160 {
    161   if (! x) fatal2(m, f);
    162   _exit(0);
    163 }
    164 static void slock(const char *f, unsigned int d, unsigned int x)
    165 {
    166   int fd;
    167 
    168   if ((fd =open_append(f)) == -1) slock_die("unable to open lock", f, x);
    169   if (d) {
    170     if (lock_ex(fd) == -1) slock_die("unable to lock", f, x);
    171     return;
    172   }
    173   if (lock_exnb(fd) == -1) slock_die("unable to lock", f, x);
    174 }
    175 
    176 static void limit(int what, long l)
    177 {
    178   struct rlimit r;
    179 
    180   if (getrlimit(what, &r) == -1) fatal("unable to getrlimit()");
    181   if ((l < 0) || (l > r.rlim_max))
    182     r.rlim_cur =r.rlim_max;
    183   else
    184     r.rlim_cur =l;
    185   if (setrlimit(what, &r) == -1) fatal("unable to setrlimit()");
    186 }
    187 static void slimit()
    188 {
    189   if (limitd >= -1) {
    190 #ifdef RLIMIT_DATA
    191     limit(RLIMIT_DATA, limitd);
    192 #else
    193     if (verbose) warn("system does not support RLIMIT_DATA");
    194 #endif
    195   }
    196   if (limits >= -1) {
    197 #ifdef RLIMIT_STACK
    198     limit(RLIMIT_STACK, limits);
    199 #else
    200     if (verbose) warn("system does not support RLIMIT_STACK");
    201 #endif
    202   }
    203   if (limitl >= -1) {
    204 #ifdef RLIMIT_MEMLOCK
    205     limit(RLIMIT_MEMLOCK, limitl);
    206 #else
    207     if (verbose) warn("system does not support RLIMIT_MEMLOCK");
    208 #endif
    209   }
    210   if (limita >= -1) {
    211 #ifdef RLIMIT_VMEM
    212     limit(RLIMIT_VMEM, limita);
    213 #else
    214 #ifdef RLIMIT_AS
    215     limit(RLIMIT_AS, limita);
    216 #else
    217     if (verbose)
    218       warn("system does neither support RLIMIT_VMEM nor RLIMIT_AS");
    219 #endif
    220 #endif
    221   }
    222   if (limito >= -1) {
    223 #ifdef RLIMIT_NOFILE
    224     limit(RLIMIT_NOFILE, limito);
    225 #else
    226 #ifdef RLIMIT_OFILE
    227     limit(RLIMIT_OFILE, limito);
    228 #else
    229     if (verbose)
    230       warn("system does neither support RLIMIT_NOFILE nor RLIMIT_OFILE");
    231 #endif
    232 #endif
    233   }
    234   if (limitp >= -1) {
    235 #ifdef RLIMIT_NPROC
    236     limit(RLIMIT_NPROC, limitp);
    237 #else
    238     if (verbose) warn("system does not support RLIMIT_NPROC");
    239 #endif
    240   }
    241   if (limitf >= -1) {
    242 #ifdef RLIMIT_FSIZE
    243     limit(RLIMIT_FSIZE, limitf);
    244 #else
    245     if (verbose) warn("system does not support RLIMIT_FSIZE");
    246 #endif
    247   }
    248   if (limitc >= -1) {
    249 #ifdef RLIMIT_CORE
    250     limit(RLIMIT_CORE, limitc);
    251 #else
    252     if (verbose) warn("system does not support RLIMIT_CORE");
    253 #endif
    254   }
    255   if (limitr >= -1) {
    256 #ifdef RLIMIT_RSS
    257     limit(RLIMIT_RSS, limitr);
    258 #else
    259     if (verbose) warn("system does not support RLIMIT_RSS");
    260 #endif
    261   }
    262   if (limitt >= -1) {
    263 #ifdef RLIMIT_CPU
    264     limit(RLIMIT_CPU, limitt);
    265 #else
    266     if (verbose) warn("system does not support RLIMIT_CPU");
    267 #endif
    268   }
    269 }
    270 
    271 /* argv[0] */
    272 static void setuidgid(int, const char *const *);
    273 static void envuidgid(int, const char *const *);
    274 static void envdir(int, const char *const *);
    275 static void pgrphack(int, const char *const *);
    276 static void setlock(int, const char *const *);
    277 static void softlimit(int, const char *const *);
    278 
    279 int main(int argc, const char **argv) {
    280   int opt;
    281   int i;
    282   unsigned long ul;
    283 
    284   progname =argv[0];
    285   for (i =str_len(progname); i; --i)
    286     if (progname[i -1] == '/') {
    287       progname +=i;
    288       break;
    289     }
    290   if (progname[0] == 'd') ++progname;
    291 
    292   /* argv[0] */
    293   if (str_equal(progname, "setuidgid")) setuidgid(argc, argv);
    294   if (str_equal(progname, "envuidgid")) envuidgid(argc, argv);
    295   if (str_equal(progname, "envdir")) envdir(argc, argv);
    296   if (str_equal(progname, "pgrphack")) pgrphack(argc, argv);
    297   if (str_equal(progname, "setlock")) setlock(argc, argv);
    298   if (str_equal(progname, "softlimit")) softlimit(argc, argv);
    299 
    300   while ((opt =getopt(argc, argv, "u:U:b:e:m:d:o:p:f:c:r:t:/:C:n:l:L:vP012V"))
    301          != opteof)
    302     switch(opt) {
    303     case 'u': set_user =(char const*)optarg; break;
    304     case 'U': env_user =(char const*)optarg; break;
    305     case 'b': argv0 =(char const*)optarg; break;
    306     case 'e': env_dir =optarg; break;
    307     case 'm':
    308       if (optarg[scan_ulong(optarg, &ul)]) usage();
    309       limits =limitl =limita =limitd =ul;
    310       break;
    311     case 'd': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitd =ul; break;
    312     case 'o': if (optarg[scan_ulong(optarg, &ul)]) usage(); limito =ul; break;
    313     case 'p': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitp =ul; break;
    314     case 'f': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitf =ul; break;
    315     case 'c': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitc =ul; break;
    316     case 'r': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitr =ul; break;
    317     case 't': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitt =ul; break;
    318     case '/': root =optarg; break;
    319     case 'C': pwd =optarg; break;
    320     case 'n':
    321       switch (*optarg) {
    322         case '-':
    323           ++optarg;
    324           if (optarg[scan_ulong(optarg, &ul)]) usage(); nicelvl =ul;
    325           nicelvl *=-1;
    326           break;
    327         case '+':
    328           ++optarg;
    329           __attribute__((fallthrough));
    330         default:
    331           if (optarg[scan_ulong(optarg, &ul)]) usage(); nicelvl =ul;
    332           break;
    333       }
    334       break;
    335     case 'l': if (lock) usage(); lock =optarg; lockdelay =1; break;
    336     case 'L': if (lock) usage(); lock =optarg; lockdelay =0; break;
    337     case 'v': verbose =1; break;
    338     case 'P': pgrp =1; break;
    339     case '0': nostdin =1; break;
    340     case '1': nostdout =1; break;
    341     case '2': nostderr =1; break;
    342     case 'V':
    343       strerr_warn1(VERSION, 0);
    344       __attribute__((fallthrough));
    345     case '?': usage();
    346     }
    347   argv +=optind;
    348   if (! argv || ! *argv) usage();
    349 
    350   if (pgrp) setsid();
    351   if (env_dir) edir(env_dir);
    352   if (root) {
    353     if (chdir(root) == -1) fatal2("unable to change directory", root);
    354     if (chroot(".") == -1) fatal("unable to change root directory");
    355   }
    356   if (pwd) {
    357     if (chdir(pwd) == -1) fatal2("unable to change directory", pwd);
    358   }
    359   if (nicelvl) {
    360     errno =0;
    361     if (nice(nicelvl) == -1) if (errno) fatal("unable to set nice level");
    362   }
    363   if (env_user) euidgid(env_user, 1);
    364   if (set_user) suidgid(set_user, 1);
    365   if (lock) slock(lock, lockdelay, 0);
    366   if (nostdin) if (close(0) == -1) fatal("unable to close stdin");
    367   if (nostdout) if (close(1) == -1) fatal("unable to close stdout");
    368   if (nostderr) if (close(2) == -1) fatal("unable to close stderr");
    369   slimit();
    370 
    371   progname =*argv;
    372   if (argv0) *argv =argv0;
    373   pathexec_env_run(progname, argv);
    374   fatal2("unable to run", *argv);
    375   return(0);
    376 }
    377 
    378 /* argv[0] */
    379 #define USAGE_SETUIDGID " account child"
    380 #define USAGE_ENVUIDGID " account child"
    381 #define USAGE_ENVDIR " dir child"
    382 #define USAGE_PGRPHACK " child"
    383 #define USAGE_SETLOCK " [ -nNxX ] file program [ arg ... ]"
    384 #define USAGE_SOFTLIMIT " [-a allbytes] [-c corebytes] [-d databytes] [-f filebytes] [-l lockbytes] [-m membytes] [-o openfiles] [-p processes] [-r residentbytes] [-s stackbytes] [-t cpusecs] child"
    385 
    386 static void setuidgid_usage()
    387 {
    388   strerr_die4x(100, "usage: ", progname, USAGE_SETUIDGID, "\n");
    389 }
    390 static void setuidgid(int argc, const char *const *argv)
    391 {
    392   const char *account;
    393 
    394   if (! (account =*++argv)) setuidgid_usage();
    395   if (! *++argv) setuidgid_usage();
    396   suidgid((char const*)account, 0);
    397   pathexec(argv);
    398   fatal2("unable to run", *argv);
    399 }
    400 
    401 static void envuidgid_usage()
    402 {
    403   strerr_die4x(100, "usage: ", progname, USAGE_ENVUIDGID, "\n");
    404 }
    405 static void envuidgid(int argc, const char *const *argv)
    406 {
    407   const char *account;
    408 
    409   if (! (account =*++argv)) envuidgid_usage();
    410   if (! *++argv) envuidgid_usage();
    411   euidgid((char const*)account, 0);
    412   pathexec(argv);
    413   fatal2("unable to run", *argv);
    414 }
    415 
    416 static void envdir_usage()
    417 {
    418   strerr_die4x(100, "usage: ", progname, USAGE_ENVDIR, "\n");
    419 }
    420 static void envdir(int argc, const char *const *argv)
    421 {
    422   const char *dir;
    423 
    424   if (! (dir =*++argv)) envdir_usage();
    425   if (! *++argv) envdir_usage();
    426   edir(dir);
    427   pathexec(argv);
    428   fatal2("unable to run", *argv);
    429 }
    430 
    431 static void pgrphack_usage()
    432 {
    433   strerr_die4x(100, "usage: ", progname, USAGE_PGRPHACK, "\n");
    434 }
    435 static void pgrphack(int argc, const char *const *argv)
    436 {
    437   if (! *++argv) pgrphack_usage();
    438   setsid();
    439   pathexec(argv);
    440   fatal2("unable to run", *argv);
    441 }
    442 
    443 static void setlock_usage()
    444 {
    445   strerr_die4x(100, "usage: ", progname, USAGE_SETLOCK, "\n");
    446 }
    447 static void setlock(int argc, const char *const *argv)
    448 {
    449   int opt;
    450   unsigned int delay =0;
    451   unsigned int x =0;
    452   const char *fn;
    453 
    454   while ((opt =getopt(argc, argv, "nNxX")) != opteof)
    455     switch(opt) {
    456       case 'n': delay =1; break;
    457       case 'N': delay =0; break;
    458       case 'x': x =1; break;
    459       case 'X': x =0; break;
    460       default: setlock_usage();
    461     }
    462   argv +=optind;
    463   if (! (fn =*argv)) setlock_usage();
    464   if (! *++argv) setlock_usage();
    465 
    466   slock(fn, delay, x);
    467   pathexec(argv);
    468   if (! x) fatal2("unable to run", *argv);
    469   _exit(0);
    470 }
    471 
    472 static void softlimit_usage()
    473 {
    474   strerr_die4x(100, "usage: ", progname, USAGE_SOFTLIMIT, "\n");
    475 }
    476 static void getlarg(long *l)
    477 {
    478   unsigned long ul;
    479 
    480   if (str_equal(optarg, "=")) { *l =-1; return; }
    481   if (optarg[scan_ulong(optarg, &ul)]) usage();
    482   *l =ul;
    483 }
    484 static void softlimit(int argc, const char *const *argv)
    485 {
    486   int opt;
    487   
    488   while ((opt =getopt(argc,argv,"a:c:d:f:l:m:o:p:r:s:t:")) != opteof)
    489     switch(opt) {
    490     case '?':
    491       softlimit_usage();
    492       __attribute__((fallthrough));
    493     case 'a': getlarg(&limita); break;
    494     case 'c': getlarg(&limitc); break;
    495     case 'd': getlarg(&limitd); break;
    496     case 'f': getlarg(&limitf); break;
    497     case 'l': getlarg(&limitl); break;
    498     case 'm': getlarg(&limitd); limits =limitl =limita =limitd; break;
    499     case 'o': getlarg(&limito); break;
    500     case 'p': getlarg(&limitp); break;
    501     case 'r': getlarg(&limitr); break;
    502     case 's': getlarg(&limits); break;
    503     case 't': getlarg(&limitt); break;
    504     }
    505   argv +=optind;
    506   if (!*argv) softlimit_usage();
    507   slimit();
    508   pathexec(argv);
    509   fatal2("unable to run", *argv);
    510 }