aboutsummaryrefslogtreecommitdiffstats
path: root/src/cmdline.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmdline.c')
-rw-r--r--src/cmdline.c228
1 files changed, 222 insertions, 6 deletions
diff --git a/src/cmdline.c b/src/cmdline.c
index bc0d676..a80c462 100644
--- a/src/cmdline.c
+++ b/src/cmdline.c
@@ -27,6 +27,11 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <pwd.h>
@@ -95,17 +100,199 @@ spawn_break(const char *message, void *user_data)
}
+/**
+ * Parse a permission string
+ *
+ * @param str The permission string
+ * @param andnot Output paramter for the mask of bits to remove (before applying `*or`)
+ * @param or Output paramter for the mask of bits to apply
+ * @return 0 on success, -1 on error
+ */
+static int
+parse_mode(const char *str, mode_t *andnot, mode_t *or)
+{
+#define U S_IRWXU
+#define G S_IRWXG
+#define O S_IRWXO
+ const char *s = str;
+ int numerical = 1;
+ char op = '=';
+ int bits;
+
+ *andnot = 0;
+ *or = 0;
+
+ if (!*s)
+ return errno = 0, -1;
+
+ for (s = str; *s; s++) {
+ if (('0' >= *s) || (*s >= '9')) {
+ numerical = 0;
+ break;
+ }
+ }
+
+ if (numerical) {
+ *andnot = U | G | O;
+ *or = atoi(str);
+ *or &= U | G | O;
+ *or = (*or & U) ? (*or | U) : (*or & ~U);
+ *or = (*or & G) ? (*or | G) : (*or & ~G);
+ *or = (*or & O) ? (*or | O) : (*or & ~O);
+ return 0;
+ }
+
+ for (s = str; *s; s++) {
+ if (strchr("+-=", *s)) {
+ op = *s;
+ } else if (strchr("ugo", *s)) {
+ if (*str == 'u')
+ bits = U;
+ else if (*str == 'g')
+ bits = G;
+ else
+ bits = O;
+ if (op == '+') {
+ *andnot |= bits;
+ *or |= bits;
+ }
+ else if (op == '-') {
+ *andnot |= bits;
+ *or &= ~bits;
+ }
+ else if (op == '=') {
+ *andnot |= U | G | O;
+ *or |= bits;
+ }
+ } else {
+ return errno = 0, -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/**
+ * Parse a user name/identifier string
+ *
+ * @param str The user's name or identifier, parsings stops
+ * a the first ':' or and the end of the srting
+ * @param uid Output parameter for the user's identifier
+ * @return 0 on success, -1 on error
+ */
+static int
+parse_uid(const char *str, uid_t *uid)
+{
+ const char *s = str;
+ int numerical = 1;
+ uid_t rc = 0;
+ struct passwd *pwd;
+
+ if (!*s || (*s == ':'))
+ return errno = 0, -1;
+
+ for (s = str; *s && (*s != ':'); s++) {
+ if (('0' >= *s) || (*s >= '9')) {
+ numerical = 0;
+ break;
+ }
+ }
+
+ if (numerical) {
+ for (s = str; !*s || (*s == ':'); s++)
+ rc = (rc * 10) + (*s & 15);
+ *uid = rc;
+ return 0;
+ }
+
+ pwd = getpwnam(str);
+ if (!pwd)
+ return -1;
+ *uid = pwd->pw_uid;
+ return 0;
+}
+
+
+/**
+ * Parse a group name/identifier string
+ *
+ * @param str The group's name or identifier
+ * @param gid Output parameter for the group's identifier
+ * @return 0 on success, -1 on error
+ */
+static int
+parse_gid(const char *str, gid_t *gid)
+{
+ const char *s = str;
+ int numerical = 1;
+ uid_t rc = 0;
+ struct group *grp;
+
+ if (!*s || strchr(s, ':'))
+ return errno = 0, -1;
+
+ for (s = str; *s; s++) {
+ if (('0' >= *s) || (*s >= '9')) {
+ numerical = 0;
+ break;
+ }
+ }
+
+ if (numerical) {
+ for (s = str; !*s || (*s == ':'); s++)
+ rc = (rc * 10) + (*s & 15);
+ *gid = rc;
+ return 0;
+ }
+
+ grp = getgrnam(str);
+ if (!grp)
+ return -1;
+ *gid = grp->gr_gid;
+ return 0;
+}
+
+
+/**
+ * Parse a ownership string
+ *
+ * @param str The ownership string
+ * @param uid Output parameter for the owner, `NULL` if `str` only contains the group
+ * @param gid Output parameter for the group, `NULL` if `str` only contains the owner
+ * @return 0 on success, -1 on error
+ */
+static int
+parse_owner(const char *str, uid_t *uid, gid_t *gid)
+{
+ int r = 0;
+
+ if (!uid)
+ return parse_gid(str, gid);
+ if (!gid)
+ return parse_uid(str, uid);
+
+ r = parse_gid(strchr(str, ':') + 1, gid);
+ if (r)
+ return r;
+ return parse_uid(str, uid);
+}
+
+
/**
* Main function of the command line interface for the bus system
*
* @param argc The number of elements in `argv`
* @param argv The command. Valid commands:
- * <argv0> create [<path>] # create a bus
- * <argv0> remove <path> # remove a bus
- * <argv0> listen <path> <command> # listen for new messages
- * <argv0> wait <path> <command> # listen for one new message
- * <argv0> broadcast <path> <message> # broadcast a message
+ * <argv0> create [<path>] # create a bus
+ * <argv0> remove <path> # remove a bus
+ * <argv0> listen <path> <command> # listen for new messages
+ * <argv0> wait <path> <command> # listen for one new message
+ * <argv0> broadcast <path> <message> # broadcast a message
+ * <argv0> chmod <mode> <path> # change permissions
+ * <argv0> chown <owner>[:<group>] <path> # change ownership
+ * <argv0> chgrp <group> <path> # change group
* <command> will be spawned with $arg set to the message
* @return 0 on sucess, 1 on error, 2 on invalid command
*/
@@ -114,6 +301,10 @@ main(int argc, char *argv[])
{
bus_t bus;
char *file;
+ struct stat attr;
+ uid_t uid;
+ gid_t gid;
+ mode_t mode_andnot, mode_or;
argv0 = *argv;
@@ -153,7 +344,30 @@ main(int argc, char *argv[])
t(bus_close(&bus));
/* TODO add -n */
- /* TODO add "chmod", "chown" and "chgrp" */
+ /* Change permissions. */
+ } else if ((argc == 4) && !strcmp(argv[1], "chmod")) { /* TODO doc */
+ t(parse_mode(argv[2], &mode_andnot, &mode_or));
+ t(stat(argv[3], &attr));
+ attr.st_mode &= ~mode_andnot;
+ attr.st_mode |= mode_or;
+ t(bus_chmod(argv[3], attr.st_mode));
+
+ /* Change ownership. */
+ } else if ((argc == 4) && !strcmp(argv[1], "chown")) { /* TODO doc */
+ if (strchr(argv[2], ':')) {
+ t(parse_owner(argv[2], &uid, &gid));
+ t(bus_chown(argv[3], uid, gid));
+ } else {
+ t(parse_owner(argv[2], &uid, NULL));
+ t(stat(argv[3], &attr));
+ t(bus_chown(argv[3], uid, attr.st_gid));
+ }
+
+ /* Change group. */
+ } else if ((argc == 4) && !strcmp(argv[1], "chgrp")) { /* TODO doc */
+ t(parse_owner(argv[2], NULL, &gid));
+ t(stat(argv[3], &attr));
+ t(bus_chown(argv[3], attr.st_uid, gid));
} else
return 2;
@@ -161,6 +375,8 @@ main(int argc, char *argv[])
return 0;
fail:
+ if (errno == 0)
+ return 2;
perror(argv0);
return 1;
}