@@ -35,6 +35,7 @@ lib_libopenvswitch_la_SOURCES = \
lib/byteq.h \
lib/cfm.c \
lib/cfm.h \
+ lib/chutil.h \
lib/classifier.c \
lib/classifier.h \
lib/classifier-private.h \
@@ -308,6 +309,7 @@ lib_libopenvswitch_la_SOURCES += \
lib/strsep.c
else
lib_libopenvswitch_la_SOURCES += \
+ lib/chutil-unix.c \
lib/daemon-unix.c \
lib/latch-unix.c \
lib/signals.c \
new file mode 100644
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include "chutil.h"
+
+#include <errno.h>
+#include <grp.h>
+#include <inttypes.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "daemon.h"
+#include "util.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(chutil_unix);
+
+#ifndef S_ISLNK
+#define S_ISLNK(mode) (0)
+#endif
+
+#define USR_MODES (S_ISUID | S_IRWXU)
+#define GRP_MODES (S_ISGID | S_IRWXG)
+#define OTH_MODES (S_IRWXO)
+#define ALL_MODES (USR_MODES | GRP_MODES | OTH_MODES)
+
+#define READ_MODES (S_IRUSR | S_IRGRP | S_IROTH)
+#define WRITE_MODES (S_IWUSR | S_IWGRP | S_IWOTH)
+#define EXEC_MODES (S_IXUSR | S_IXGRP | S_IXOTH)
+
+#define SUID_MODES (S_ISUID | S_ISGID)
+
+/* Convert a chown-style string to uid/gid; supports numeric arguments
+ * as well as usernames. */
+int
+ovs_strtousr(const char *user_spec, uid_t *uid, char **user, gid_t *gid,
+ bool validate_user_group)
+{
+ char *pos = strchr(user_spec, ':');
+ size_t bufsize = 0;
+ user_spec += strspn(user_spec, " \t\r\n");
+
+ size_t len = pos ? pos - user_spec : strlen(user_spec);
+ char *buf = NULL;
+ struct passwd pwd, *res = NULL;
+ int e;
+
+ buf = x2nrealloc(NULL, &bufsize, sizeof pwd);
+ char *user_search = NULL;
+ uid_t uid_search = getuid();
+ if (len) {
+ user_search = xmemdup0(user_spec, len);
+ if (!strcspn(user_search, "0123456789")) {
+ uid_search = strtoul(user_search, NULL, 10);
+ free(user_search);
+ user_search = NULL;
+ }
+ }
+
+ if (user_search) {
+ while ((e = getpwnam_r(user_search, &pwd, buf,
+ bufsize * sizeof pwd, &res)) == ERANGE) {
+ buf = x2nrealloc(buf, &bufsize, sizeof pwd);
+ }
+ } else {
+ while ((e = getpwuid_r(uid_search, &pwd, buf, bufsize * sizeof pwd,
+ &res)) == ERANGE) {
+ buf = x2nrealloc(buf, &bufsize, sizeof pwd);
+ }
+ }
+
+ if (!res && !e) {
+ e = ENOENT;
+ }
+
+ if (e) {
+ VLOG_ERR("Failed to retrieve user pwentry (%s), aborting.",
+ ovs_strerror(e));
+ goto release;
+ }
+
+ if (!user_search) {
+ user_search = xstrdup(pwd.pw_name);
+ }
+
+ if (user) {
+ *user = user_search;
+ }
+
+ if (uid) {
+ *uid = pwd.pw_uid;
+ }
+
+ if (gid) {
+ *gid = pwd.pw_gid;
+ }
+
+ if (pos) {
+ gid_t tmpgid = pwd.pw_gid;
+ char *grpstr = pos + 1;
+ grpstr += strspn(grpstr, " \t\r\n");
+
+ if (*grpstr) {
+ struct group grp, *res;
+
+ bufsize = 1;
+ buf = x2nrealloc(buf, &bufsize, sizeof grp);
+
+ if (strcspn(grpstr, "0123456789")) {
+ while ((e = getgrnam_r(grpstr, &grp, buf,
+ bufsize * sizeof grp, &res))
+ == ERANGE) {
+ buf = x2nrealloc(buf, &bufsize, sizeof grp);
+ }
+ } else {
+ gid_t grpgid = strtoul(grpstr, NULL, 10);
+ while ((e = getgrgid_r(grpgid, &grp, buf,
+ bufsize * sizeof grp, &res))
+ == ERANGE) {
+ buf = x2nrealloc(buf, &bufsize, sizeof grp);
+ }
+ }
+
+ if (!res && !e) {
+ e = ENOENT;
+ }
+
+ if (e) {
+ VLOG_ERR("Failed to get group entry for %s (%s), aborting.",
+ grpstr, ovs_strerror(e));
+ goto release;
+ }
+
+ if (tmpgid != grp.gr_gid) {
+ char **mem;
+
+ for (mem = grp.gr_mem; *mem; ++mem) {
+ if (!strcmp(*mem, user_search)) {
+ break;
+ }
+ }
+
+ if (!*mem && validate_user_group) {
+ VLOG_ERR("Invalid user str %s (user %s is not in "
+ "group %s), aborting.", user_spec,
+ user_search, grpstr);
+ e = EINVAL;
+ goto release;
+ }
+ if (gid) {
+ *gid = grp.gr_gid;
+ }
+ }
+ }
+ }
+
+release:
+ free(buf);
+ if (e || !user) {
+ free(user_search);
+ }
+ return e;
+}
+
+/* Convert a chmod style string (or set of comma separated chmod style
+ * strings) to a mode_t.
+ */
+static mode_t
+chmod_getmode(const char *mode, mode_t oldmode)
+{
+ mode_t ret = oldmode & ALL_MODES;
+ if (*mode >= '0' && *mode <= '7') {
+ ret = 0;
+
+ while (*mode >= '0' && *mode <= '7') {
+ ret = (ret << 3) | (*mode++ - '0');
+ }
+
+ if (*mode) {
+ errno = EINVAL;
+ return 0;
+ }
+ } else {
+ while (*mode) {
+ mode_t actors_mask = 0, perms_mask = 0;
+ char action = 0;
+ while (*mode && !action) {
+ switch (*mode++) {
+ case 'a':
+ actors_mask |= ALL_MODES;
+ break;
+ case 'u':
+ actors_mask |= USR_MODES;
+ break;
+ case 'g':
+ actors_mask |= GRP_MODES;
+ break;
+ case 'o':
+ actors_mask |= OTH_MODES;
+ break;
+ case '+':
+ case '-':
+ case '=':
+ action = *(mode-1);
+ break;
+ default:
+ errno = EINVAL;
+ return 0;
+ }
+ }
+ if (!actors_mask) {
+ actors_mask = USR_MODES;
+ }
+ while (*mode) {
+ switch(*mode++) {
+ case 'r':
+ perms_mask |= READ_MODES;
+ break;
+ case 'w':
+ perms_mask |= WRITE_MODES;
+ break;
+ case 'x':
+ perms_mask |= EXEC_MODES;
+ break;
+ case 's':
+ perms_mask |= SUID_MODES;
+ break;
+ case ',':
+ goto actions;
+ default:
+ errno = EINVAL;
+ return 0;
+ }
+ }
+actions:
+ if (action == '+') {
+ ret |= actors_mask & perms_mask;
+ } else if (action == '-') {
+ ret &= ~(actors_mask & perms_mask);
+ } else if (action == '=') {
+ ret &= ~(actors_mask & (READ_MODES | WRITE_MODES
+ | EXEC_MODES));
+ ret |= actors_mask & perms_mask;
+ }
+ }
+ }
+ return ret;
+}
+
+
+/* Changes the mode of a file to the mode specified. Accepts chmod style
+ * comma-separated strings. Returns 0 on success, otherwise a positive errno
+ * value. */
+int
+ovs_fchmod(int fd, const char *mode)
+{
+ mode_t new_mode;
+ struct stat st;
+ int err;
+
+ if (fstat(fd, &st)) {
+ err = errno;
+ VLOG_ERR("ovs_fchown: fstat (%s)", ovs_strerror(errno));
+ return err;
+ }
+
+ if (S_ISLNK(st.st_mode)) {
+ errno = EINVAL;
+ err = errno;
+ VLOG_ERR("ovs_fchown: unable to change ownership of symlink");
+ return err;
+ }
+
+ errno = 0;
+ new_mode = chmod_getmode(mode, st.st_mode);
+ if (errno) {
+ err = errno;
+ VLOG_ERR("ovs_fchmod bad mode (%s) specified (%s)", mode,
+ ovs_strerror(errno));
+ return err;
+ }
+
+ if (fchmod(fd, new_mode)) {
+ VLOG_ERR("ovs_fchmod: chmod error (%s) with mode %s",
+ ovs_strerror(errno), mode);
+ return errno;
+ }
+ return 0;
+}
+
+
+/* Changes the ownership of a file to the mode specified. Accepts chown style
+ * user:group strings. Returns 0 on success. Non-zero results contain
+ * errno. */
+int
+ovs_fchown(int fd, const char *owner)
+{
+ struct stat st;
+ uid_t user;
+ gid_t group;
+ int err;
+
+ if (fstat(fd, &st)) {
+ err = errno;
+ VLOG_ERR("ovs_fchmod: lstat (%s)", ovs_strerror(errno));
+ return err;
+ }
+
+ if (S_ISLNK(st.st_mode) || S_ISDIR(st.st_mode)) {
+ errno = EINVAL;
+ err = errno;
+ VLOG_ERR("ovs_fchmod: changing symlink / directory modes is not "
+ "supported.");
+ return err;
+ }
+
+ if (ovs_strtousr(owner, &user, NULL, &group, true)) {
+ err = errno;
+ VLOG_ERR("ovs_fchown: unknown user or group - bailing");
+ return err;
+ }
+
+ if (fchown(fd, user, group)) {
+ err = errno;
+ VLOG_ERR("ovs_fchown: chown error (%s)", ovs_strerror(errno));
+ return err;
+ }
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CHUTIL_H
+#define CHUTIL_H 1
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include "compiler.h"
+
+#ifndef WIN32
+int ovs_fchmod(int fd, const char *mode) OVS_WARN_UNUSED_RESULT;
+int ovs_fchown(int fd, const char *usrstr) OVS_WARN_UNUSED_RESULT;
+int ovs_strtousr(const char *user_spec, uid_t *uid, char **user,
+ gid_t *gid, bool validate_user_group) OVS_WARN_UNUSED_RESULT;
+#endif
+
+#endif
@@ -15,6 +15,7 @@
*/
#include <config.h>
+#include "chutil.h"
#include "daemon.h"
#include "daemon-private.h"
#include <errno.h>
@@ -874,58 +875,6 @@ daemon_become_new_user(bool access_datapath)
}
}
-/* Return the maximun suggested buffer size for both getpwname_r()
- * and getgrnam_r().
- *
- * This size may still not be big enough. in case getpwname_r()
- * and friends return ERANGE, a larger buffer should be supplied to
- * retry. (The man page did not specify the max size to stop at, we
- * will keep trying with doubling the buffer size for each round until
- * the size wrapps around size_t. */
-static size_t
-get_sysconf_buffer_size(void)
-{
- size_t bufsize, pwd_bs = 0, grp_bs = 0;
- const size_t default_bufsize = 1024;
-
- errno = 0;
- if ((pwd_bs = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) {
- if (errno) {
- VLOG_FATAL("%s: Read initial passwordd struct size "
- "failed (%s), aborting. ", pidfile,
- ovs_strerror(errno));
- }
- }
-
- if ((grp_bs = sysconf(_SC_GETGR_R_SIZE_MAX)) == -1) {
- if (errno) {
- VLOG_FATAL("%s: Read initial group struct size "
- "failed (%s), aborting. ", pidfile,
- ovs_strerror(errno));
- }
- }
-
- bufsize = MAX(pwd_bs, grp_bs);
- return bufsize ? bufsize : default_bufsize;
-}
-
-/* Try to double the size of '*buf', return true
- * if successful, and '*sizep' will be updated with
- * the new size. Otherwise, return false. */
-static bool
-enlarge_buffer(char **buf, size_t *sizep)
-{
- size_t newsize = *sizep * 2;
-
- if (newsize > *sizep) {
- *buf = xrealloc(*buf, newsize);
- *sizep = newsize;
- return true;
- }
-
- return false;
-}
-
/* Parse and sanity check user_spec.
*
* If successful, set global variables 'uid' and 'gid'
@@ -940,10 +889,6 @@ enlarge_buffer(char **buf, size_t *sizep)
void
daemon_set_new_user(const char *user_spec)
{
- char *pos = strchr(user_spec, ':');
- size_t init_bufsize, bufsize;
-
- init_bufsize = get_sysconf_buffer_size();
uid = getuid();
gid = getgid();
@@ -951,94 +896,10 @@ daemon_set_new_user(const char *user_spec)
VLOG_FATAL("%s: only root can use --user option", pidfile);
}
- user_spec += strspn(user_spec, " \t\r\n");
- size_t len = pos ? pos - user_spec : strlen(user_spec);
- char *buf;
- struct passwd pwd, *res;
- int e;
-
- bufsize = init_bufsize;
- buf = xmalloc(bufsize);
- if (len) {
- user = xmemdup0(user_spec, len);
-
- while ((e = getpwnam_r(user, &pwd, buf, bufsize, &res)) == ERANGE) {
- if (!enlarge_buffer(&buf, &bufsize)) {
- break;
- }
- }
-
- if (e != 0) {
- VLOG_FATAL("%s: Failed to retrive user %s's uid (%s), aborting.",
- pidfile, user, ovs_strerror(e));
- }
- if (res == NULL) {
- VLOG_FATAL("%s: user %s not found, aborting.", pidfile, user);
- }
+ if (!ovs_strtousr(user_spec, &uid, &user, &gid, true)) {
+ switch_user = true;
} else {
- /* User name is not specified, use current user. */
- while ((e = getpwuid_r(uid, &pwd, buf, bufsize, &res)) == ERANGE) {
- if (!enlarge_buffer(&buf, &bufsize)) {
- break;
- }
- }
-
- if (e != 0) {
- VLOG_FATAL("%s: Failed to retrive current user's name "
- "(%s), aborting.", pidfile, ovs_strerror(e));
- }
- user = xstrdup(pwd.pw_name);
+ VLOG_FATAL("%s: Failed --user option with %s, aborting.", pidfile,
+ user_spec);
}
-
- uid = pwd.pw_uid;
- gid = pwd.pw_gid;
- free(buf);
-
- if (pos) {
- char *grpstr = pos + 1;
- grpstr += strspn(grpstr, " \t\r\n");
-
- if (*grpstr) {
- struct group grp, *res;
-
- bufsize = init_bufsize;
- buf = xmalloc(bufsize);
- while ((e = getgrnam_r(grpstr, &grp, buf, bufsize, &res))
- == ERANGE) {
- if (!enlarge_buffer(&buf, &bufsize)) {
- break;
- }
- }
-
- if (e) {
- VLOG_FATAL("%s: Failed to get group entry for %s, "
- "(%s), aborting.", pidfile, grpstr,
- ovs_strerror(e));
- }
- if (res == NULL) {
- VLOG_FATAL("%s: group %s not found, aborting.", pidfile,
- grpstr);
- }
-
- if (gid != grp.gr_gid) {
- char **mem;
-
- for (mem = grp.gr_mem; *mem; ++mem) {
- if (!strcmp(*mem, user)) {
- break;
- }
- }
-
- if (!*mem) {
- VLOG_FATAL("%s: Invalid --user option %s (user %s is "
- "not in group %s), aborting.", pidfile,
- user_spec, user, grpstr);
- }
- gid = grp.gr_gid;
- }
- free(buf);
- }
- }
-
- switch_user = true;
}
@@ -174,6 +174,7 @@ valgrind_wrappers = \
tests/valgrind/test-atomic \
tests/valgrind/test-bundle \
tests/valgrind/test-byte-order \
+ tests/valgrind/test-chutil \
tests/valgrind/test-classifier \
tests/valgrind/test-ccmap \
tests/valgrind/test-cmap \
@@ -361,6 +362,7 @@ tests_ovstest_SOURCES = \
if !WIN32
tests_ovstest_SOURCES += \
+ tests/test-chutil.c \
tests/test-unix-socket.c
endif
@@ -240,3 +240,8 @@ AT_CLEANUP
AT_SETUP([rcu])
AT_CHECK([ovstest test-rcu-quiesce], [0], [])
AT_CLEANUP
+
+AT_SETUP([test chutil functions])
+AT_SKIP_IF([test "$IS_WIN32" = "yes"])
+AT_CHECK([ovstest test-chutil], [0], [ignore], [ignore])
+AT_CLEANUP
new file mode 100644
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2016 Red Hat, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* A non-exhaustive test for some of the functions and macros declared in
+ * the change-utils suite in chutil.h. */
+
+#include <config.h>
+#undef NDEBUG
+
+#include <assert.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "chutil.h"
+#include "ovstest.h"
+#include "util.h"
+
+static int
+get_mode(const char *pathname, mode_t *mode)
+{
+ struct stat st;
+ if (stat(pathname, &st)) {
+ return -1;
+ }
+ *mode = st.st_mode & 0x7ff;
+ return 0;
+}
+
+static int
+with_temp_file(int (*fn)(const char *pathname, int fd))
+{
+ char filepath[PATH_MAX] = "/tmp/test_chutil_wtfXXXXXX";
+ mode_t old_mask = umask(0777);
+ int fd = mkstemp(filepath);
+ umask(old_mask);
+ assert(fd >= 0);
+ int result = fn(filepath, fd);
+ close(fd);
+ unlink(filepath);
+ return result;
+}
+
+static int
+run_chmod_bad_parsing(const char *pathname, int fd)
+{
+ static char users[] = "bcdefhijklmnpqrstvwxyz";
+ static char perms[] = "abcdefghijklmnopqtuvyz";
+ static char actions[] = "~`!@#$%^&*()_";
+
+ char *itest;
+
+ mode_t pathmode;
+ if (get_mode(pathname, &pathmode)) {
+ return -1;
+ }
+
+ for (itest = users; itest != users + strlen(users); ++itest) {
+ char buf[256] = {0};
+ mode_t testmode;
+ snprintf(buf, sizeof(buf), "%c+rwx", *itest);
+ if (!ovs_fchmod(fd, buf) || get_mode(pathname, &testmode)
+ || testmode != pathmode) {
+ printf("F(%s)", buf);
+ return -1;
+ }
+ }
+
+ for (itest = perms; itest != perms + strlen(perms); ++itest) {
+ char buf[256] = {0};
+ mode_t testmode;
+ snprintf(buf, sizeof(buf), "u+%c", *itest);
+ if (!ovs_fchmod(fd, buf) || get_mode(pathname, &testmode)
+ || testmode != pathmode) {
+ printf("F(%s)", buf);
+ return -1;
+ }
+ }
+
+ for (itest = actions; itest != actions + strlen(actions); ++itest) {
+ char buf[256] = {0};
+ mode_t testmode;
+ snprintf(buf, sizeof(buf), "u%crw", *itest);
+ if (!ovs_fchmod(fd, buf) || get_mode(pathname, &testmode)
+ || testmode != pathmode) {
+ printf("F(%s)", buf);
+ return -1;
+ }
+ }
+ printf(".");
+ return 0;
+}
+
+/* Skip suid and sgid for now. */
+static int
+run_chmod_str_successes(const char *pathname, int fd)
+{
+ const char *users[] = { "u", "g", "o", "a", "ug", "uo", "go" };
+ const char *perms[] = { "r", "w", "x", "rw", "rx", "wx" };
+ size_t iusers, iperms;
+ mode_t chkmode;
+
+ if (get_mode(pathname, &chkmode)) {
+ return -1;
+ }
+
+ for (iusers = 0; iusers < ARRAY_SIZE(users); ++iusers) {
+ for (iperms = 0; iperms < ARRAY_SIZE(perms); ++iperms) {
+ mode_t pathmode;
+ char buf[256] = {0};
+ snprintf(buf, sizeof(buf), "%s+%s", users[iusers], perms[iperms]);
+ if (ovs_fchmod(fd, buf) || get_mode(pathname, &pathmode)) {
+ printf("run_chmod_successes:E(%s)\n", buf);
+ return -1;
+ }
+ /* XXX: Check the actual mode here */
+ snprintf(buf, sizeof(buf), "%s-%s", users[iusers], perms[iperms]);
+ if (ovs_fchmod(fd, buf) || get_mode(pathname, &pathmode)
+ || pathmode != chkmode) {
+ printf("run_chmod_successes:F(%s:%x:%x)\n", buf, pathmode,
+ chkmode);
+ return -1;
+ }
+ }
+ }
+
+ mode_t pmode;
+ if (ovs_fchmod(fd, "u-rwx,g-rwx,o-rwx")
+ || get_mode(pathname, &pmode) || pmode != 0) {
+ printf("run_chmod_successes:csvF\n");
+ return -1;
+ }
+
+ if (ovs_fchmod(fd, "u=rx,g=w") || get_mode(pathname, &pmode)
+ || pmode != (S_IRUSR | S_IXUSR | S_IWGRP)) {
+ printf("run_chmod_successes:assignF\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+run_chmod_numeric_successes(const char *pathname, int fd)
+{
+ const char *modestrs[] = {"0755", "0644", "0600", "11", "20", "755",
+ "640"};
+ const mode_t expectedmode[] = {
+ S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH,
+ S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR,
+ S_IRUSR | S_IWUSR,
+ S_IXOTH | S_IXGRP,
+ S_IWGRP,
+ S_IRWXU | S_IRGRP | S_IROTH | S_IXGRP | S_IXOTH,
+ S_IRUSR | S_IRGRP | S_IWUSR,
+ };
+ size_t imodes;
+ for (imodes = 0; imodes < ARRAY_SIZE(modestrs); ++imodes) {
+ mode_t newmode;
+ if (ovs_fchmod(fd, modestrs[imodes]) ||
+ get_mode(pathname, &newmode)) {
+ printf("run_chmod_numeric_successes:F(%s)\n", modestrs[imodes]);
+ return -1;
+ }
+ if (newmode != expectedmode[imodes]) {
+ printf("run_chmod_numeric_successes:F(%x:%x)\n", newmode,
+ expectedmode[imodes]);
+ return -1;
+ }
+ if (ovs_fchmod(fd, "0000")) {
+ printf("run_chmod_numeric_successes:E(%s)\n", modestrs[imodes]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+run_ovs_strtouser_successes(void)
+{
+ /* seems this is the only user:group combination to exist? */
+ const char *ugparses[] = {"root:", "root:root", "0:0", "0:", "nobody:",
+ "root", "nobody"};
+ size_t iugstr;
+ for (iugstr = 0; iugstr < ARRAY_SIZE(ugparses); ++iugstr) {
+ uid_t ui = 1;
+ gid_t gi = 1;
+ char *user = NULL;
+ if (ovs_strtousr(ugparses[iugstr], &ui, &user, &gi, true) ||
+ !user || (strcmp("root", user) && strcmp("nobody", user))) {
+ printf("run_ovs_strtouser_successes:F(%s)\n", ugparses[iugstr]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+run_ovs_strtouser_failures(void)
+{
+ /* If any of these are successful, you have a poorly configured system
+ * so this test 'failing' is the least of your worries. */
+ const char *ugparses[] = {"nobody:root", "THISUSERBETTERNOTEXSIST:",
+ ":THISGROUPBETTERNOTEXIST"};
+ size_t iugstr;
+ for (iugstr = 0; iugstr < ARRAY_SIZE(ugparses); ++iugstr) {
+ if (!ovs_strtousr(ugparses[iugstr], NULL, NULL, NULL, true)) {
+ printf("run_ovs_strtouser_failures:F(%s)\n", ugparses[iugstr]);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void
+test_chutil_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
+{
+ assert(!with_temp_file(run_chmod_bad_parsing));
+ assert(!with_temp_file(run_chmod_str_successes));
+ assert(!with_temp_file(run_chmod_numeric_successes));
+ assert(!run_ovs_strtouser_successes());
+ assert(!run_ovs_strtouser_failures());
+ printf("\n");
+}
+
+OVSTEST_REGISTER("test-chutil", test_chutil_main);