From d69291c1c8d37a4ab133b91906bd694a41fc9319 Mon Sep 17 00:00:00 2001 From: Teemu Toivola Date: Mon, 23 Jun 2014 22:04:07 +0300 Subject: [PATCH] add parameters for changing daemon process user and group, increase informativity of daemon error messages --- src/common.c | 18 +++++ src/common.h | 3 + src/vnstatd.c | 177 +++++++++++++++++++++++++++++++++++++++---- src/vnstatd.h | 6 +- tests/common_tests.c | 30 ++++++++ 5 files changed, 217 insertions(+), 17 deletions(-) diff --git a/src/common.c b/src/common.c index dc10c61..3cc2e2e 100644 --- a/src/common.c +++ b/src/common.c @@ -225,3 +225,21 @@ inline char *strncpy_nt(char *dest, const char *src, size_t n) dest[n-1] = '\0'; return dest; } + +int isnumeric(const char *s) +{ + int i, len; + len = strlen(s); + + if (!len) { + return 0; + } + + for (i=0; i #include #include +#include +#include #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) || defined(__FreeBSD_kernel__) #include @@ -277,6 +279,7 @@ uint64_t countercalc(const uint64_t *a, const uint64_t *b); void addtraffic(uint64_t *destmb, int *destkb, const uint64_t srcmb, const int srckb); uint64_t mbkbtokb(uint64_t mb, uint64_t kb); inline char *strncpy_nt(char *dest, const char *src, size_t n); +int isnumeric(const char *s); /* global variables */ DATA data; diff --git a/src/vnstatd.c b/src/vnstatd.c index 247990b..d5c2041 100644 --- a/src/vnstatd.c +++ b/src/vnstatd.c @@ -29,6 +29,7 @@ int main(int argc, char *argv[]) int dbsaved = 1, showhelp = 1, sync = 0, saveinterval, forcesave = 0, noadd = 0; uint32_t dbhash = 0; char cfgfile[512], dirname[512]; + char user[33], group[33]; DIR *dir; struct dirent *di; datanode *datalist; @@ -38,6 +39,8 @@ int main(int argc, char *argv[]) debug = 0; /* debug disabled by default */ rundaemon = 0; /* daemon disabled by default */ cfgfile[0] = '\0'; + user[0] = '\0'; + group[0] = '\0'; prevdbupdate = prevdbsave = dbcount = 0; /* early check for debug and config parameter */ @@ -90,6 +93,28 @@ int main(int argc, char *argv[]) showhelp = 0; } else if ((strcmp(argv[currentarg],"-s")==0) || (strcmp(argv[currentarg],"--sync")==0)) { sync = 1; + } else if ((strcmp(argv[currentarg],"-u")==0) || (strcmp(argv[currentarg],"--user")==0)) { + if (currentarg+10) { /* parent exits */ exit(EXIT_SUCCESS); @@ -473,7 +504,7 @@ void daemonize(void) setsid(); /* obtain a new process group */ if (cfg.uselogging) { - snprintf(errorstring, 512, "vnStat daemon %s started.", VNSTATVERSION); + snprintf(errorstring, 512, "vnStat daemon %s started. (uid:%d gid:%d)", VNSTATVERSION, (int)getuid(), (int)getgid()); if (!printe(PT_Info)) { printf("Error: Unable to use logfile. Exiting.\n"); exit(EXIT_FAILURE); @@ -483,14 +514,14 @@ void daemonize(void) /* lock / pid file */ pidfile = open(cfg.pidfile, O_RDWR|O_CREAT, 0644); if (pidfile<0) { - perror("pidfile"); - snprintf(errorstring, 512, "pidfile failed, exiting."); + perror("Error: pidfile"); + snprintf(errorstring, 512, "opening pidfile \"%s\" failed (%s), exiting.", cfg.pidfile, strerror(errno)); printe(PT_Error); exit(EXIT_FAILURE); /* can't open */ } if (lockf(pidfile,F_TLOCK,0)<0) { - perror("pidfile lock"); - snprintf(errorstring, 512, "pidfile lock failed, exiting."); + perror("Error: pidfile lock"); + snprintf(errorstring, 512, "pidfile \"%s\" lock failed (%s), exiting.", cfg.pidfile, strerror(errno)); printe(PT_Error); exit(EXIT_FAILURE); /* can't lock */ } @@ -507,14 +538,14 @@ void daemonize(void) /* stdout */ if (dup(i) < 0) { - perror("dup(stdout)"); + perror("Error: dup(stdout)"); snprintf(errorstring, 512, "dup(stdout) failed, exiting."); printe(PT_Error); exit(EXIT_FAILURE); } /* stderr */ if (dup(i) < 0) { - perror("dup(stderr)"); + perror("Error: dup(stderr)"); snprintf(errorstring, 512, "dup(stderr) failed, exiting."); printe(PT_Error); exit(EXIT_FAILURE); @@ -524,7 +555,7 @@ void daemonize(void) /* change running directory */ if (chdir("/") < 0) { - perror("chdir(/)"); + perror("Error: chdir(/)"); snprintf(errorstring, 512, "directory change to / failed, exiting."); printe(PT_Error); exit(EXIT_FAILURE); @@ -533,9 +564,9 @@ void daemonize(void) /* first instance continues */ snprintf(str, 10, "%d\n", (int)getpid()); - /* record pid to lockfile */ + /* record pid to pidfile */ if (write(pidfile,str,strlen(str)) < 0) { - perror("write(pidfile)"); + perror("Error: write(pidfile)"); snprintf(errorstring, 512, "writing to pidfile %s failed, exiting.", cfg.pidfile); printe(PT_Error); exit(EXIT_FAILURE); @@ -620,7 +651,7 @@ int addinterfaces(const char *dirname) return count; } -void debugtimestamp() +void debugtimestamp(void) { time_t now; char timestamp[22]; @@ -629,3 +660,117 @@ void debugtimestamp() strftime(timestamp, 22, "%Y-%m-%d %H:%M:%S", localtime(&now)); printf("%s\n", timestamp); } + +uid_t getuser(const char *user) +{ + struct passwd *pw; + uid_t uid; + + if (!strlen(user)) { + return getuid(); + } + + if (isnumeric(user)) { + uid = atoi(user); + pw = getpwuid(uid); + } else { + pw = getpwnam(user); + } + + if (pw == NULL) { + printf("Error: No such user: \"%s\".\n", user); + exit(EXIT_FAILURE); + } + + uid = pw->pw_uid; + + if (debug) + printf("getuser(%s / %d): %s (%d)\n", user, atoi(user), pw->pw_name, (int)uid); + + return uid; +} + +gid_t getgroup(const char *group) +{ + struct group *gr; + gid_t gid; + + if (!strlen(group)) { + return getgid(); + } + + if (isnumeric(group)) { + gid = atoi(group); + gr = getgrgid(gid); + } else { + gr = getgrnam(group); + } + + if (gr == NULL) { + printf("Error: No such group: \"%s\".\n", group); + exit(EXIT_FAILURE); + } + + gid = gr->gr_gid; + + if (debug) + printf("getgroup(%s / %d): %s (%d)\n", group, atoi(group), gr->gr_name, (int)gid); + + return gid; +} + +void setuser(const char *user) +{ + uid_t uid; + + if (!strlen(user)) { + return; + } + + if (getuid() != 0 && geteuid() != 0) { + printf("Error: User can only be set as root.\n"); + exit(EXIT_FAILURE); + } + + if (isnumeric(user) && atoi(user) == 0) { + return; + } + + uid = getuser(user); + + if (debug) + printf("switching to user id %d.\n", uid); + + if (setuid(uid) != 0) { + perror("Error: setuid"); + exit(EXIT_FAILURE); + } +} + +void setgroup(const char *group) +{ + gid_t gid; + + if (!strlen(group)) { + return; + } + + if (getuid() != 0 && geteuid() != 0) { + printf("Error: Group can only be set as root.\n"); + exit(EXIT_FAILURE); + } + + if (isnumeric(group) && atoi(group) == 0) { + return; + } + + gid = getgroup(group); + + if (debug) + printf("switching to group id %d.\n", gid); + + if (setgid(gid) != 0) { + perror("Error: setgid"); + exit(EXIT_FAILURE); + } +} diff --git a/src/vnstatd.h b/src/vnstatd.h index 14a6688..d62414f 100644 --- a/src/vnstatd.h +++ b/src/vnstatd.h @@ -3,6 +3,10 @@ void daemonize(void); int addinterfaces(const char *dirname); -void debugtimestamp(); +void debugtimestamp(void); +uid_t getuser(const char *user); +gid_t getgroup(const char *group); +void setuser(const char *user); +void setgroup(const char *group); #endif diff --git a/tests/common_tests.c b/tests/common_tests.c index 0a8dcca..a228f91 100644 --- a/tests/common_tests.c +++ b/tests/common_tests.c @@ -304,6 +304,33 @@ START_TEST(strncpy_nt_with_over_maximum_length_string) } END_TEST +START_TEST(isnumeric_empty) +{ + ck_assert_int_eq(isnumeric(""), 0); +} +END_TEST + +START_TEST(isnumeric_it_is) +{ + ck_assert_int_eq(isnumeric("0"), 1); + ck_assert_int_eq(isnumeric("1"), 1); + ck_assert_int_eq(isnumeric("12"), 1); + ck_assert_int_eq(isnumeric("123"), 1); +} +END_TEST + +START_TEST(isnumeric_it_is_not) +{ + ck_assert_int_eq(isnumeric("a"), 0); + ck_assert_int_eq(isnumeric("abc"), 0); + ck_assert_int_eq(isnumeric("a1"), 0); + ck_assert_int_eq(isnumeric("1a"), 0); + ck_assert_int_eq(isnumeric("123abc"), 0); + ck_assert_int_eq(isnumeric("/"), 0); + ck_assert_int_eq(isnumeric("-"), 0); +} +END_TEST + void add_common_tests(Suite *s) { /* Common test cases */ @@ -327,5 +354,8 @@ void add_common_tests(Suite *s) tcase_add_test(tc_common, strncpy_nt_with_below_maximum_length_string); tcase_add_test(tc_common, strncpy_nt_with_maximum_length_string); tcase_add_test(tc_common, strncpy_nt_with_over_maximum_length_string); + tcase_add_test(tc_common, isnumeric_empty); + tcase_add_test(tc_common, isnumeric_it_is); + tcase_add_test(tc_common, isnumeric_it_is_not); suite_add_tcase(s, tc_common); } -- 2.40.0