perf daemon: Add client socket support
authorJiri Olsa <jolsa@kernel.org>
Mon, 8 Feb 2021 20:08:49 +0000 (21:08 +0100)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 11 Feb 2021 12:52:28 +0000 (09:52 -0300)
Add support for client socket side that will be used to send commands to
the daemon server socket.

This patch adds only the core support, all commands using this
functionality are coming in the following patches.

Committer notes:

Hat to patch patch it to deal with this in some systems:

  cc1: warnings being treated as errors
  builtin-daemon.c: In function 'send_cmd':  MKDIR    /tmp/build/perf/bench/

  builtin-daemon.c:1368: error: ignoring return value of 'fwrite', declared with attribute warn_unused_result
    MKDIR    /tmp/build/perf/tests/
  make[3]: *** [/tmp/build/perf/builtin-daemon.o] Error 1

And also to not leak the 'line' buffer allocated by getline(), since you
initialized line to NULL and len to zero, man page says:

  If *lineptr is set to NULL and *n is set 0 before the call,
  then getline() will allocate a buffer for storing the line.
  This buffer should be freed by the user program even if
  getline() failed.

Signed-off-by: Jiri Olsa <jolsa@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexei Budankov <abudankov@huawei.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Michael Petlan <mpetlan@redhat.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Link: https://lore.kernel.org/r/20210208200908.1019149-6-jolsa@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/builtin-daemon.c

index 495e4ff..0a282c4 100644 (file)
@@ -13,6 +13,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
+#include <sys/stat.h>
 #include <poll.h>
 #include "builtin.h"
 #include "perf.h"
@@ -44,6 +45,67 @@ static void sig_handler(int sig __maybe_unused)
        done = true;
 }
 
+static int client_config(const char *var, const char *value, void *cb)
+{
+       struct daemon *daemon = cb;
+
+       if (!strcmp(var, "daemon.base") && !daemon->base_user) {
+               daemon->base = strdup(value);
+               if (!daemon->base)
+                       return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static int check_base(struct daemon *daemon)
+{
+       struct stat st;
+
+       if (!daemon->base) {
+               pr_err("failed: base not defined\n");
+               return -EINVAL;
+       }
+
+       if (stat(daemon->base, &st)) {
+               switch (errno) {
+               case EACCES:
+                       pr_err("failed: permission denied for '%s' base\n",
+                              daemon->base);
+                       return -EACCES;
+               case ENOENT:
+                       pr_err("failed: base '%s' does not exists\n",
+                              daemon->base);
+                       return -EACCES;
+               default:
+                       pr_err("failed: can't access base '%s': %s\n",
+                              daemon->base, strerror(errno));
+                       return -errno;
+               }
+       }
+
+       if ((st.st_mode & S_IFMT) != S_IFDIR) {
+               pr_err("failed: base '%s' is not directory\n",
+                      daemon->base);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int setup_client_config(struct daemon *daemon)
+{
+       struct perf_config_set *set = perf_config_set__load_file(daemon->config_real);
+       int err = -ENOMEM;
+
+       if (set) {
+               err = perf_config_set(set, client_config, daemon);
+               perf_config_set__delete(set);
+       }
+
+       return err ?: check_base(daemon);
+}
+
 static int setup_server_socket(struct daemon *daemon)
 {
        struct sockaddr_un addr;
@@ -130,6 +192,38 @@ out:
        return ret;
 }
 
+static int setup_client_socket(struct daemon *daemon)
+{
+       struct sockaddr_un addr;
+       char path[PATH_MAX];
+       int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+
+       if (fd == -1) {
+               perror("failed: socket");
+               return -1;
+       }
+
+       scnprintf(path, sizeof(path), "%s/control", daemon->base);
+
+       if (strlen(path) + 1 >= sizeof(addr.sun_path)) {
+               pr_err("failed: control path too long '%s'\n", path);
+               close(fd);
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       strlcpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
+
+       if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
+               perror("failed: connect");
+               close(fd);
+               return -1;
+       }
+
+       return fd;
+}
+
 static void daemon__exit(struct daemon *daemon)
 {
        free(daemon->config_real);
@@ -222,6 +316,50 @@ out:
        return err;
 }
 
+__maybe_unused
+static int send_cmd(struct daemon *daemon, union cmd *cmd)
+{
+       int ret = -1, fd;
+       char *line = NULL;
+       size_t len = 0;
+       ssize_t nread;
+       FILE *in = NULL;
+
+       if (setup_client_config(daemon))
+               return -1;
+
+       fd = setup_client_socket(daemon);
+       if (fd < 0)
+               return -1;
+
+       if (sizeof(*cmd) != writen(fd, cmd, sizeof(*cmd))) {
+               perror("failed: write");
+               goto out;
+       }
+
+       in = fdopen(fd, "r");
+       if (!in) {
+               perror("failed: fdopen");
+               goto out;
+       }
+
+       while ((nread = getline(&line, &len, in)) != -1) {
+               if (fwrite(line, nread, 1, stdout) != 1)
+                       goto out_fclose;
+               fflush(stdout);
+       }
+
+       ret = 0;
+out_fclose:
+       fclose(in);
+       free(line);
+out:
+       /* If in is defined, then fd is closed via fclose. */
+       if (!in)
+               close(fd);
+       return ret;
+}
+
 int cmd_daemon(int argc, const char **argv)
 {
        struct option daemon_options[] = {