summaryrefslogtreecommitdiff
path: root/src/supervision/s6-svperms.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/supervision/s6-svperms.c')
-rw-r--r--src/supervision/s6-svperms.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/src/supervision/s6-svperms.c b/src/supervision/s6-svperms.c
new file mode 100644
index 0000000..178ea08
--- /dev/null
+++ b/src/supervision/s6-svperms.c
@@ -0,0 +1,272 @@
+/* ISC license. */
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <skalibs/types.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+
+#include <s6/s6-supervise.h>
+
+#define USAGE "s6-svperms [ -v ] [ -u | -g group | -G group | -o | -O group ] [ -e | -E group ] servicedir..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static gid_t scangid (char const *s)
+{
+ if (s[0] == ':')
+ {
+ gid_t g ;
+ if (!gid0_scan(s+1, &g)) dieusage() ;
+ return g ;
+ }
+ else
+ {
+ struct group *gr ;
+ errno = 0 ;
+ gr = getgrnam(s) ;
+ if (!gr)
+ {
+ if (errno) strerr_diefu1sys(111, "getgrnam") ;
+ else strerr_diefu3x(100, "find entry for ", s, " in group database") ;
+ }
+ return gr->gr_gid ;
+ }
+}
+
+static char *gidname (gid_t gid)
+{
+ struct group *gr ;
+ errno = 0 ;
+ gr = getgrgid(gid) ;
+ if (!gr)
+ {
+ static char fmt[GID_FMT] ;
+ fmt[gid_fmt(fmt, gid)] = 0 ;
+ if (errno) strerr_warnwu2sys("getgrgid ", fmt) ;
+ return fmt ;
+ }
+ return gr->gr_name ;
+}
+
+static void out (char const *s)
+{
+ if (buffer_puts(buffer_1, s) < 0)
+ strerr_diefu1sys(111, "write to stdout") ;
+}
+
+static inline int printsupervise (char const *dir)
+{
+ struct stat st ;
+ size_t len = strlen(dir) ;
+ char fn[len + sizeof(S6_SUPERVISE_CTLDIR) + 9] ;
+ memcpy(fn, dir, len) ;
+ memcpy(fn + len, "/" S6_SUPERVISE_CTLDIR, sizeof(S6_SUPERVISE_CTLDIR) + 1) ;
+ if (stat(fn, &st) < 0)
+ {
+ strerr_warnwu2sys("stat ", fn) ;
+ return 1 ;
+ }
+ if (!S_ISDIR(st.st_mode))
+ {
+ strerr_warnw2x(fn, " is not a directory") ;
+ return 1 ;
+ }
+ if (st.st_mode & 05066 || (st.st_mode & 0700) != 0700 || ((st.st_mode & 0001) && !(st.st_mode & 0010)))
+ {
+ char fmt[UINT_OFMT] ;
+ fmt[uint_ofmt(fmt, st.st_mode & 07777)] = 0 ;
+ strerr_warnw3x(fn, " has incorrect permissions: ", fmt) ;
+ return 1 ;
+ }
+ out(dir) ;
+ out(" status: ") ;
+ if (st.st_mode & 0011)
+ {
+ if (st.st_mode & 0001) buffer_puts(buffer_1, "public") ;
+ else
+ {
+ out("group ") ;
+ out(gidname(st.st_gid)) ;
+ }
+ }
+ else out("owner") ;
+ out("\n") ;
+ memcpy(fn + len + sizeof(S6_SUPERVISE_CTLDIR), "/control", 9) ;
+ if (stat(fn, &st) < 0)
+ {
+ strerr_warnwu2sys("stat ", fn) ;
+ return 1 ;
+ }
+ if (!S_ISFIFO(st.st_mode))
+ {
+ strerr_warnw2x(fn, " is not a named pipe") ;
+ return 1 ;
+ }
+ if (st.st_mode & 0157)
+ {
+ char fmt[UINT_OFMT] ;
+ fmt[uint_ofmt(fmt, st.st_mode & 07777)] = 0 ;
+ strerr_warnw3x(fn, " has incorrect permissions: ", fmt) ;
+ return 1 ;
+ }
+ out(dir) ;
+ out(" control: ") ;
+ if (st.st_mode & 0020)
+ {
+ out("group ") ;
+ out(gidname(st.st_gid)) ;
+ }
+ else out("owner") ;
+ out("\n") ;
+ return 0 ;
+}
+
+static inline int printevent (char const *dir)
+{
+ struct stat st ;
+ size_t len = strlen(dir) ;
+ char fn[len + sizeof(S6_SUPERVISE_EVENTDIR) + 1] ;
+ memcpy(fn, dir, len) ;
+ memcpy(fn + len, "/" S6_SUPERVISE_EVENTDIR, sizeof(S6_SUPERVISE_EVENTDIR) + 1) ;
+ if (stat(fn, &st) < 0)
+ {
+ strerr_warnwu2sys("stat ", fn) ;
+ return 1 ;
+ }
+ if (!S_ISDIR(st.st_mode))
+ {
+ strerr_warnw2x(fn, " is not a directory") ;
+ return 1 ;
+ }
+ if ((st.st_mode & 07777) != 01733 && (st.st_mode & 07777) != 03730)
+ {
+ char fmt[UINT_OFMT] ;
+ fmt[uint_ofmt(fmt, st.st_mode & 07777)] = 0 ;
+ strerr_warnw3x(fn, " has incorrect permissions: ", fmt) ;
+ return 1 ;
+ }
+ out(dir) ;
+ out(" events: ") ;
+ if ((st.st_mode & 07777) == 03730)
+ {
+ out("group ") ;
+ out(gidname(st.st_gid)) ;
+ }
+ else out("public") ;
+ out("\n") ;
+ return 0 ;
+}
+
+static gid_t primarygid (char const *fn)
+{
+ struct passwd *pw ;
+ struct stat st ;
+ if (stat(fn, &st) < 0) strerr_diefu2sys(111, "stat ", fn) ;
+ errno = 0 ;
+ pw = getpwuid(st.st_uid) ;
+ if (!pw)
+ {
+ strerr_warnwu3sys("determine primary gid for the owner of ", fn, " (using root instead)") ;
+ return 0 ;
+ }
+ else return pw->pw_gid ;
+}
+
+static inline void modsupervise (char const *dir, unsigned int what, gid_t gid)
+{
+ size_t len = strlen(dir) ;
+ gid_t cgid = 0 ;
+ mode_t mode = 0700 ;
+ char fn[len + sizeof(S6_SUPERVISE_CTLDIR) + 9] ;
+ memcpy(fn, dir, len) ;
+ memcpy(fn + len, "/" S6_SUPERVISE_CTLDIR, sizeof(S6_SUPERVISE_CTLDIR) + 1) ;
+ switch (what & 3)
+ {
+ case 0 : cgid = primarygid(fn) ; mode = 0700 ; break ;
+ case 1 : cgid = gid ; mode = 0710 ; break ;
+ case 2 : cgid = primarygid(fn) ; mode = 0711 ; break ;
+ }
+ if (chown(fn, -1, cgid) < 0)
+ strerr_diefu2sys(111, "chown ", fn) ;
+ if (chmod(fn, mode) < 0)
+ strerr_diefu2sys(111, "chmod ", fn) ;
+ memcpy(fn + len + sizeof(S6_SUPERVISE_CTLDIR), "/control", 9) ;
+ if (what & 4) mode = 0620 ;
+ else
+ {
+ gid = primarygid(fn) ;
+ mode = 0600 ;
+ }
+ if (chown(fn, -1, gid) < 0)
+ strerr_diefu2sys(111, "chown ", fn) ;
+ if (chmod(fn, mode) < 0)
+ strerr_diefu2sys(111, "chmod ", fn) ;
+}
+
+static inline void modevent (char const *dir, gid_t gid)
+{
+ size_t len = strlen(dir) ;
+ mode_t mode ;
+ char fn[len + sizeof(S6_SUPERVISE_EVENTDIR) + 1] ;
+ memcpy(fn, dir, len) ;
+ memcpy(fn + len, "/" S6_SUPERVISE_EVENTDIR, sizeof(S6_SUPERVISE_EVENTDIR) + 1) ;
+ if (gid == (gid_t)-1)
+ {
+ gid = primarygid(fn) ;
+ mode = 01733 ;
+ }
+ else mode = 03730 ;
+ if (chown(fn, -1, gid) < 0)
+ strerr_diefu2sys(111, "chown ", fn) ;
+ if (chmod(fn, mode) < 0)
+ strerr_diefu2sys(111, "chmod ", fn) ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ int e = 0 ;
+ gid_t gid = -1 ;
+ gid_t eventgid = -1 ;
+ int rw = 0 ;
+ unsigned int what = 0 ;
+ PROG = "s6-svperms" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "vug:G:oO:eE:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'v' : rw |= 1 ; break ;
+ case 'u' : rw |= 2 ; what = 0 ; break ;
+ case 'g' : rw |= 2 ; what = 1 ; gid = scangid(l.arg) ; break ;
+ case 'G' : rw |= 2 ; what = 5 ; gid = scangid(l.arg) ; break ;
+ case 'o' : rw |= 2 ; what = 2 ; break ;
+ case 'O' : rw |= 2 ; what = 6 ; gid = scangid(l.arg) ; break ;
+ case 'e' : rw |= 4 ; eventgid = -1 ; break ;
+ case 'E' : rw |= 4 ; eventgid = scangid(l.arg) ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) dieusage() ;
+
+ if (!rw) rw = 1 ;
+ for (; *argv ; argv++)
+ {
+ if (rw & 2) modsupervise(*argv, what, gid) ;
+ if (rw & 4) modevent(*argv, eventgid) ;
+ if (rw & 1) { e |= printsupervise(*argv) ; e |= printevent(*argv) ; }
+ }
+ if (rw & 1 && !buffer_flush(buffer_1))
+ strerr_diefu1sys(111, "write to stdout") ;
+ return e ;
+}