runit

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

commit 70f5d2a7a817ca1cbe74d1dfd4b41f36118826d5
parent fd19242436a79f98df55670ea586aec7490c9681
Author: Gerrit Pape <pape@smarden.org>
Date:   Tue,  5 Aug 2003 20:41:19 +0000

  * uidgid.c, uidgid.h: new; get uid/gid by name.
  * chpst: new; run program with a changed process state (includes envdir,
    envuidgid, pgrphack, setlock, setuidgid, softlimit functionality).

Diffstat:
Aman/chpst.8 | 194+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackage/CHANGES | 7+++++++
Msrc/Makefile | 13++++++++-----
Msrc/TARGETS | 5+++--
Asrc/chpst.c | 418+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/uidgid.c | 31+++++++++++++++++++++++++++++++
Asrc/uidgid.h | 11+++++++++++
7 files changed, 672 insertions(+), 7 deletions(-)

diff --git a/man/chpst.8 b/man/chpst.8 @@ -0,0 +1,194 @@ +.TH chpst 8 +.SH NAME +chpst \- runs a program with a changed process state +.SH SYNOPSIS +.B chpst +[\-vP012] +[\-u +.IR user ] +[\-U +.IR user ] +[-e +.IR dir ] +[-l|-L +.IR lock ] +[-m +.IR bytes ] +[-o +.IR n ] +[-p +.IR n ] +[-f +.IR bytes ] +[-c +.IR bytes ] +.I prog +.SH DESCRIPTION +.I prog +consists of one or more arguments. +.P +.B chpst +changes the process state according to the given options, and runs +.IR prog . +.SH OPTIONS +.TP +.B \-u \fIuser[:group] +setuidgid. +Set uid and gid to the +.IR user 's +uid and gid. +If +.I user +is followed by a colon and a +.IR group , +set the gid to +.IR group 's +gid instead of +.IR user 's +gid. +All supplementary groups are removed. +.TP +.B \-U \fIuser[:group] +envuidgid. +Set the environment variables $UID and $GID to the +.IR user 's +uid and gid. +If +.I user +is followed by a colon and a +.IR group , +set $GID to the +.IR group 's +gid instead of +.IR user 's +gid. +.TP +.B \-e \fIdir +envdir. +Set various environment variables as specified by files in the directory +.IR dir : +If +.I dir +contains a file named +.I k +whose first line is +.IR v , +.B chpst +removes the environment variable +.I k +if it exists, and then adds the environment variable +.I k +with the value +.IR v . +The name +.I k +must not contain =. +Spaces and tabs at the end of +.I v +are removed, and nulls in +.I v +are changed to newlines. +If the file +.I k +is empty (0 bytes long), +.B chpst +removes the environment variable +.I k +if it exists, without adding a new variable. +.TP +.B \-l \fIlock +lock. +Open the file +.I lock +for writing, and obtain an exclusive lock on it. +.I lock +will be created if it does not exist. +If +.I lock +is locked by another process, wait until a new lock can be obtained. +.TP +.B \-L \fIlock +The same as \-l, but fail immediately if +.I lock +is locked by another process. +.TP +.B \-m \fIbytes +limit memory. +Limit the data segment, stack segment, and locked physical pages, per +process to +.I bytes +bytes each. +.TP +.B \-o \fIn +limit open files. +Limit the number of open file descriptors per process to +.IR n . +.TP +.B \-p \fIn +limit processes. +Limit the number of processes per uid to +.IR n . +.TP +.B \-f \fIbytes +limit output size. +Limit the output file size to +.I bytes +bytes. +.TP +.B \-c \fIbytes +limit core size. +Limit the core file size to +.I bytes bytes. +.TP +.B \-v +verbose. +Print warnings about limits unsupported by the system to standard error. +.TP +.B \-P +pgrphack. +Run +.I prog +in a new process group. +.TP +.B \-0 +Close standard input before running +.IR prog . +.TP +.B \-1 +Close standard output before running +.IR prog . +.TP +.B \-2 +Close standard error before running +.IR prog . +.SH EXIT CODES +.B chpst +exits 100 when called with wrong options. +It prints an error message and exits 111 if it has trouble changing the +process state. +Otherwise its exit code is the same as that of +.IR prog . +.SH EMULATION +If +.B chpst +is called as +.BR envdir (8), +.BR envuidgid (8), +.BR pgrphack (8), +.BR setlock (8), +.BR setuidgid (8), +or +.BR softlimit (8), +it emulates the functionality of these programs from the daemontools package +respectively. +.SH SEE ALSO +runsv(8), +runsvctrl(8), +runsvstat(8), +runsvdir(8), +setsid(2) +.P + http://smarden.org/runit/ + http://cr.yp.to/daemontools.html +.SH AUTHOR +Gerrit Pape <pape@smarden.org> diff --git a/package/CHANGES b/package/CHANGES @@ -1,3 +1,10 @@ +runit 0.11.0 + + * uidgid.c, uidgid.h: new; get uid/gid by name. + * chpst: new; run program with a changed process state (includes envdir, + envuidgid, pgrphack, setlock, setuidgid, softlimit functionality). + XXX * setuidgid.c: remove; obsolete (replaced by chpst). + runit 0.10.0 Sun, 22 Jun 2003 20:44:58 +0200 * doc/index.html, doc/install.html, doc/replaceinit.html, doc/useinit.html: diff --git a/src/Makefile b/src/Makefile @@ -1,5 +1,5 @@ IT=sysdeps runit runit-init runsv runsvctrl runsvstat runsvdir runsvchdir \ -svwaitup svwaitdown utmpset svlogd setuidgid +svwaitup svwaitdown utmpset svlogd chpst default: $(IT) @@ -36,8 +36,8 @@ runsvchdir: load runsvchdir.o unix.a byte.a svlogd: load svlogd.o pmatch.o fmt_ptime.o unix.a byte.a time.a ./load svlogd pmatch.o fmt_ptime.o unix.a byte.a time.a -setuidgid: load setuidgid.o unix.a byte.a - ./load setuidgid unix.a byte.a +chpst: load chpst.o uidgid.o unix.a byte.a + ./load chpst uidgid.o unix.a byte.a runit.o: compile sysdeps runit.c ./compile runit.c @@ -72,10 +72,13 @@ runsvchdir.o: compile runsvchdir.c svlogd.o: compile sysdeps svlogd.c ./compile svlogd.c -setuidgid.o: compile setuidgid.c - ./compile setuidgid.c +chpst.o: compile sysdeps chpst.c + ./compile chpst.c +uidgid.o: compile uidgid.c uidgid.h + ./compile uidgid.c + pmatch.o: compile pmatch.c ./compile pmatch.c diff --git a/src/TARGETS b/src/TARGETS @@ -20,10 +20,11 @@ runsvchdir runsvchdir.o svlogd svlogd.o -setuidgid -setuidgid.o +chpst +chpst.o pmatch.o fmt_ptime.o +uidgid.o alloc.o alloc_re.o buffer.o diff --git a/src/chpst.c b/src/chpst.c @@ -0,0 +1,418 @@ +#include <sys/types.h> +#include <time.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> +#include "sgetopt.h" +#include "error.h" +#include "strerr.h" +#include "str.h" +#include "uidgid.h" +#include "prot.h" +#include "strerr.h" +#include "scan.h" +#include "fmt.h" +#include "lock.h" +#include "pathexec.h" +#include "stralloc.h" +#include "byte.h" +#include "open.h" +#include "openreadclose.h" +#include "direntry.h" + +#define USAGE_MAIN " [-vP012] [-u user[:group]] [-U user[:group]] [-e dir] [-l|-L lock] [-m n] [-o n] [-p n] [-f n] [-c n] prog" +#define FATAL "chpst: fatal: " +#define WARNING "chpst: warning: " + +const char *progname; +static stralloc sa; + +void fatal(const char *m) { strerr_die3sys(111, FATAL, m, ": "); } +void fatal2(const char *m0, const char *m1) { + strerr_die5sys(111, FATAL, m0, ": ", m1, ": "); +} +void fatalx(const char *m0, const char *m1) { + strerr_die4x(111, FATAL, m0, ": ", m1); +} +void warn(const char *m) { strerr_warn2(WARNING, m, 0); } +void die_nomem() { strerr_die2x(111, FATAL, "out of memory."); } +void usage() { strerr_die4x(100, "usage: ", progname, USAGE_MAIN, "\n"); } + +char *set_user =0; +char *env_user =0; +const char *env_dir =0; +unsigned int verbose =0; +unsigned int pgrp =0; +unsigned int nostdin =0; +unsigned int nostdout =0; +unsigned int nostderr =0; +long limitd =-2; +long limits =-2; +long limitl =-2; +long limita =-2; +long limito =-2; +long limitp =-2; +long limitf =-2; +long limitc =-2; +long limitr =-2; +long limitt =-2; +const char *lock =0; +unsigned int lockdelay; + +void suidgid(char *user, unsigned int dogrp) { + struct uidgid ugid; + + if (! uidgid_get(&ugid, user, dogrp)) { + if (dogrp) + fatalx("unknown user/group", user); + else + fatalx("unknown account", user); + } + if (prot_gid(ugid.gid) == -1) fatal("unable to setgid"); + if (prot_uid(ugid.uid) == -1) fatal("unable to setuid"); +} + +void euidgid(char *user, unsigned int dogrp) { + struct uidgid ugid; + char bufnum[FMT_ULONG]; + + if (! uidgid_get(&ugid, user, dogrp)) { + if (dogrp) + fatalx("unknown user/group", user); + else + fatalx("unknown account", user); + } + bufnum[fmt_ulong(bufnum, ugid.gid)] =0; + if (! pathexec_env("GID", bufnum)) die_nomem(); + bufnum[fmt_ulong(bufnum, ugid.uid)] =0; + if (! pathexec_env("UID", bufnum)) die_nomem(); +} + +void edir(const char *dirname) { + int wdir; + DIR *dir; + direntry *d; + int i; + + if (! (wdir =open_read("."))) + fatal("unable to open current working direcotry"); + if (chdir(dirname)) fatal2("unable to switch to directory", dirname); + if (! (dir =opendir("."))) fatal2("unable to open directory", dirname); + for (;;) { + errno =0; + d =readdir(dir); + if (! d) { + if (errno) fatal2("unable to read directory", dirname); + break; + } + if (d->d_name[0] == '.') continue; + if (openreadclose(d->d_name, &sa, 256) == -1) + strerr_die6sys(111, FATAL, "unable to read ", dirname, "/", + d->d_name, ": "); + if (sa.len) { + sa.len =byte_chr(sa.s, sa.len, '\n'); + while (sa.len && (sa.s[sa.len -1] == ' ' || sa.s[sa.len -1] == '\t')) + --sa.len; + for (i =0; i < sa.len; ++i) if (! sa.s[i]) sa.s[i] ='\n'; + if (! stralloc_0(&sa)) die_nomem(); + if (! pathexec_env(d->d_name, sa.s)) die_nomem(); + } + else + if (! pathexec_env(d->d_name, 0)) die_nomem(); + } + closedir(dir); + if (fchdir(wdir) == -1) fatal("unable to switch to starting directory"); + close(wdir); +} + +void slock(const char *f, unsigned int d, unsigned int x) { + int fd; + void die(const char *m) { + if (! x) fatal2(m, f); + _exit(0); + } + + if ((fd =open_append(f)) == -1) die("unable to open lock"); + if (d) { + if (lock_ex(fd) == -1) die("unable to lock"); + return; + } + if (lock_exnb(fd) == -1) die("unable to lock"); +} + +void slimit() { + void limit(int what, long l) { + struct rlimit r; + + if (getrlimit(what, &r) == -1) fatal("unable to getrlimit()"); + if ((l < 0) || (l > r.rlim_max)) + r.rlim_cur =r.rlim_max; + else + r.rlim_cur =l; + if (setrlimit(what, &r) == -1) fatal("unable to setrlimit()"); + } + + if (limitd >= -1) { +#ifdef RLIMIT_DATA + limit(RLIMIT_DATA, limitd); +#else + if (verbose) warn("system does not support RLIMIT_DATA"); +#endif + } + if (limits >= -1) { +#ifdef RLIMIT_STACK + limit(RLIMIT_STACK, limits); +#else + if (verbose) warn("system does not support RLIMIT_STACK"); +#endif + } + if (limitl >= -1) { +#ifdef RLIMIT_MEMLOCK + limit(RLIMIT_MEMLOCK, limitl); +#else + if (verbose) warn("system does not support RLIMIT_MEMLOCK"); +#endif + } + if (limita >= -1) { +#ifdef RLIMIT_VMEM + limit(RLIMIT_VMEM, limita); +#else +#ifdef RLIMIT_AS + limit(RLIMIT_AS, limita); +#else + if (verbose) + warn("system does neither support RLIMIT_VMEM nor RLIMIT_AS"); +#endif +#endif + } + if (limito >= -1) { +#ifdef RLIMIT_NOFILE + limit(RLIMIT_NOFILE, limito); +#else +#ifdef RLIMIT_OFILE + limit(RLIMIT_OFILE, limito); +#else + if (verbose) + warn("system does neither support RLIMIT_NOFILE nor RLIMIT_OFILE"); +#endif +#endif + } + if (limitp >= -1) { +#ifdef RLIMIT_NPROC + limit(RLIMIT_NPROC, limitp); +#else + if (verbose) warn("system does not support RLIMIT_NPROC"); +#endif + } + if (limitf >= -1) { +#ifdef RLIMIT_FSIZE + limit(RLIMIT_FSIZE, limitf); +#else + if (verbose) warn("system does not support RLIMIT_FSIZE"); +#endif + } + if (limitc >= -1) { +#ifdef RLIMIT_CORE + limit(RLIMIT_CORE, limitc); +#else + if (verbose) warn("system does not support RLIMIT_CORE"); +#endif + } + if (limitr >= -1) { +#ifdef RLIMIT_RSS + limit(RLIMIT_RSS, limitr); +#else + if (verbose) warn("system does not support RLIMIT_RSS"); +#endif + } + if (limitt >= -1) { +#ifdef RLIMIT_CPU + limit(RLIMIT_CPU, limitt); +#else + if (verbose) warn("system does not support RLIMIT_CPU"); +#endif + } +} + +/* argv[0] */ +void setuidgid(int, const char *const *); +void envuidgid(int, const char *const *); +void envdir(int, const char *const *); +void pgrphack(int, const char *const *); +void setlock(int, const char *const *); +void softlimit(int, const char *const *); + +int main(int argc, const char *const *argv) { + int opt; + int i; + + progname =argv[0]; + for (i =str_len(progname); i; --i) + if (progname[i -1] == '/') { + progname +=i; + break; + } + if (progname[0] == 'd') ++progname; + + /* argv[0] */ + if (str_equal(progname, "setuidgid")) setuidgid(argc, argv); + if (str_equal(progname, "envuidgid")) envuidgid(argc, argv); + if (str_equal(progname, "envdir")) envdir(argc, argv); + if (str_equal(progname, "pgrphack")) pgrphack(argc, argv); + if (str_equal(progname, "setlock")) setlock(argc, argv); + if (str_equal(progname, "softlimit")) softlimit(argc, argv); + + while ((opt =getopt(argc, argv, "u:U:e:m:o:p:f:c:r:t:l:L:vP012V")) != opteof) + switch(opt) { + case 'u': set_user =(char*)optarg; break; + case 'U': env_user =(char*)optarg; break; + case 'e': env_dir =optarg; break; + case 'm': + if (optarg[scan_ulong(optarg, &limitd)]) usage(); + limits =limitl =limita =limitd; + break; + case 'o': if (optarg[scan_ulong(optarg, &limito)]) usage(); break; + case 'p': if (optarg[scan_ulong(optarg, &limitp)]) usage(); break; + case 'f': if (optarg[scan_ulong(optarg, &limitf)]) usage(); break; + case 'c': if (optarg[scan_ulong(optarg, &limitc)]) usage(); break; + case 'r': if (optarg[scan_ulong(optarg, &limitr)]) usage(); break; + case 't': if (optarg[scan_ulong(optarg, &limitt)]) usage(); break; + case 'l': if (lock) usage(); lock =optarg; lockdelay =1; break; + case 'L': if (lock) usage(); lock =optarg; lockdelay =0; break; + case 'v': verbose =1; break; + case 'P': pgrp =1; break; + case '0': nostdin =1; break; + case '1': nostdout =1; break; + case '2': nostderr =1; break; + case 'V': strerr_warn1("$Id$", 0); + case '?': usage(); + } + argv +=optind; + if (! argv || ! *argv) usage(); + + if (pgrp) setsid(); + if (env_dir) edir(env_dir); + if (env_user) euidgid(env_user, 1); + if (set_user) suidgid(set_user, 1); + slimit(); + if (lock) slock(lock, lockdelay, 0); + if (nostdin) if (close(0) == -1) fatal("unable to close stdin"); + if (nostdout) if (close(1) == -1) fatal("unable to close stdout"); + if (nostderr) if (close(2) == -1) fatal("unable to close stderr"); + + pathexec(argv); + fatal2("unable to run", *argv); + return(0); +} + +/* argv[0] */ +#define USAGE_SETUIDGID " account child" +#define USAGE_ENVUIDGID " account child" +#define USAGE_ENVDIR " dir child" +#define USAGE_PGRPHACK " child" +#define USAGE_SETLOCK " [ -nNxX ] file program [ arg ... ]" +#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" + +void setuidgid(int argc, const char *const *argv) { + const char *account; + void usage() { + strerr_die4x(100, "usage: ", progname, USAGE_SETUIDGID, "\n"); + } + + if (! (account =*++argv)) usage(); + if (! *++argv) usage(); + suidgid((char*)account, 0); + pathexec(argv); + fatal2("unable to run", *argv); +} +void envuidgid(int argc, const char *const *argv) { + const char *account; + void usage() { + strerr_die4x(100, "usage: ", progname, USAGE_ENVUIDGID, "\n"); + } + + if (! (account =*++argv)) usage(); + if (! *++argv) usage(); + euidgid((char*)account, 0); + pathexec(argv); + fatal2("unable to run", *argv); +} +void envdir(int argc, const char *const *argv) { + const char *dir; + void usage() { + strerr_die4x(100, "usage: ", progname, USAGE_ENVDIR, "\n"); + } + + if (! (dir =*++argv)) usage(); + if (! *++argv) usage(); + edir(dir); + pathexec(argv); + fatal2("unable to run", *argv); +} +void pgrphack(int argc, const char *const *argv) { + void usage() { + strerr_die4x(100, "usage: ", progname, USAGE_PGRPHACK, "\n"); + } + + if (! *++argv) usage(); + setsid(); + pathexec(argv); + fatal2("unable to run", *argv); +} +void setlock(int argc, const char *const *argv) { + int opt; + unsigned int delay =0; + unsigned int x =0; + const char *fn; + void usage() { + strerr_die4x(100, "usage: ", progname, USAGE_SETLOCK, "\n"); + } + + while ((opt =getopt(argc, argv, "nNxX")) != opteof) + switch(opt) { + case 'n': delay =1; break; + case 'N': delay =0; break; + case 'x': x =1; break; + case 'X': x =0; break; + default: usage(); + } + argv +=optind; + if (! (fn =*argv)) usage(); + if (! *++argv) usage(); + + slock(fn, delay, x); + pathexec(argv); + if (! x) fatal2("unable to run", *argv); + _exit(0); +} +void softlimit(int argc, const char *const *argv) { + int opt; + void usage() { + strerr_die4x(100, "usage: ", progname, USAGE_SOFTLIMIT, "\n"); + } + void getlarg(long *l) { + if (str_equal(optarg, "=")) *l =-1; + else if (optarg[scan_ulong(optarg, l)]) usage(); + } + + while ((opt =getopt(argc,argv,"a:c:d:f:l:m:o:p:r:s:t:")) != opteof) + switch(opt) { + case '?': usage(); + case 'a': getlarg(&limita); break; + case 'c': getlarg(&limitc); break; + case 'd': getlarg(&limitd); break; + case 'f': getlarg(&limitf); break; + case 'l': getlarg(&limitl); break; + case 'm': getlarg(&limitd); limits =limitl =limita =limitd; break; + case 'o': getlarg(&limito); break; + case 'p': getlarg(&limitp); break; + case 'r': getlarg(&limitr); break; + case 's': getlarg(&limits); break; + case 't': getlarg(&limitt); break; + } + argv +=optind; + if (!*argv) usage(); + slimit(); + pathexec(argv); + fatal2("unable to run", *argv); +} diff --git a/src/uidgid.c b/src/uidgid.c @@ -0,0 +1,31 @@ +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include "uidgid.h" +#include "str.h" + +unsigned int uidgid_get(struct uidgid *u, char *ug, unsigned int dogrp) { + char *g =0; + struct passwd *pwd =0; + struct group *gr =0; + int d =0; + + if (dogrp) + if (ug[(d =str_chr(ug, ':'))] == ':') { + ug[d] =0; + g =ug +d +1; + } + pwd =getpwnam(ug); + if (g) { + gr =getgrnam(g); + ug[d] =':'; + } + if (! pwd) return(0); + if (g && ! gr) return(0); + if (gr) + u->gid =gr->gr_gid; + else + u->gid =pwd->pw_gid; + u->uid =pwd->pw_uid; + return(1); +} diff --git a/src/uidgid.h b/src/uidgid.h @@ -0,0 +1,11 @@ +#ifndef UIDGID_H +#define UIDGID_H + +struct uidgid { + int uid; + int gid; +}; + +extern unsigned int uidgid_get(struct uidgid *, char *, unsigned int); + +#endif