]> git.ipfire.org Git - thirdparty/lldpd.git/blobdiff - src/client/commands.c
lldpctl: put a lock around some commands to avoid race conditions
[thirdparty/lldpd.git] / src / client / commands.c
index b96b77f8dae011f21919f52250f6aa98412e4c54..905652ee073997895d26f6ce35af3d5831450a26 100644 (file)
@@ -18,6 +18,9 @@
 #include "client.h"
 #include <string.h>
 #include <sys/queue.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
 
 /**
  * An element of the environment (a key and a value).
@@ -68,6 +71,7 @@ struct cmd_node {
        const char *token;      /**< Token to enter this cnode */
        const char *doc;        /**< Documentation string */
        int privileged;         /**< Privileged command? */
+       int lock;               /**< Lock required for execution? */
        int hidden;             /**< Hidden command? */
 
        /**
@@ -113,6 +117,21 @@ commands_privileged(struct cmd_node *node)
        return node;
 }
 
+/**
+ * Make a node accessible only with a lock.
+ *
+ * @param node node to use lock to execute
+ * @return the modified node
+ *
+ * The node is modified. It is returned to ease chaining.
+ */
+struct cmd_node*
+commands_lock(struct cmd_node *node)
+{
+       if (node) node->lock = 1;
+       return node;
+}
+
 /**
  * Hide a node from help or completion.
  *
@@ -344,6 +363,7 @@ _commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
        int n, rc = 0, completion = (word != NULL);
        int help = 0;           /* Are we asking for help? */
        int complete = 0;       /* Are we asking for possible completions? */
+       int needlock = 0;       /* Do we need a lock? */
        struct cmd_env env = {
                .elements = TAILQ_HEAD_INITIALIZER(env.elements),
                .stack = TAILQ_HEAD_INITIALIZER(env.stack),
@@ -388,6 +408,7 @@ _commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
                                    !strcmp(candidate->token, token)) {
                                        /* Exact match */
                                        best = candidate;
+                                       needlock = needlock || candidate->lock;
                                        break;
                                }
                                if (!best) best = candidate;
@@ -406,6 +427,7 @@ _commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
                                if (!candidate->token &&
                                    CAN_EXECUTE(candidate)) {
                                        best = candidate;
+                                       needlock = needlock || candidate->lock;
                                        break;
                                }
                        }
@@ -421,9 +443,37 @@ _commands_execute(struct lldpctl_conn_t *conn, struct writer *w,
 
                /* Push and execute */
                cmdenv_push(&env, best);
-               if (best->execute && best->execute(conn, w, &env, best->arg) != 1) {
-                       rc = -1;
-                       goto end;
+               if (best->execute) {
+                       int lockfd;
+                       struct sockaddr_un su;
+                       if (needlock) {
+                               log_debug("lldpctl", "getting lock for %s", ctlname);
+                               if ((lockfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
+                                       log_warn("lldpctl", "cannot open for lock %s", ctlname);
+                                       rc = -1;
+                                       goto end;
+                               }
+                               su.sun_family = AF_UNIX;
+                               strlcpy(su.sun_path, ctlname, sizeof(su.sun_path));
+                               if (connect(lockfd, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) {
+                                       log_warn("lldpctl", "cannot connect to socket %s", ctlname);
+                                       rc = -1;
+                                       close(lockfd);
+                                       goto end;
+                               }
+                               if (lockf(lockfd, F_LOCK, 0) == -1) {
+                                       log_warn("lldpctl", "cannot get lock on %s", ctlname);
+                                       rc = -1;
+                                       close(lockfd);
+                                       goto end;
+                               }
+                       }
+                       if (best->execute(conn, w, &env, best->arg) != 1) {
+                               rc = -1;
+                               if (needlock) close(lockfd);
+                               goto end;
+                       }
+                       close(lockfd);
                }
                env.argp++;
        }