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:
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