commit:     2c6dff36d4a6d604e803e59db4d3ac6496577274
Author:     Michał Górny <mgorny <AT> gentoo <DOT> org>
AuthorDate: Sat Nov 29 20:15:54 2025 +0000
Commit:     Michał Górny <mgorny <AT> gentoo <DOT> org>
CommitDate: Sat Nov 29 20:15:54 2025 +0000
URL:        https://gitweb.gentoo.org/proj/steve.git/commit/?id=2c6dff36

Implement load average ioctls

Signed-off-by: Michał Górny <mgorny <AT> gentoo.org>

 steve.cxx  | 56 +++++++++++++++++++++++++++++-------------------------
 stevie.cxx | 64 ++++++++++++++++++++++++++++++++++++++++++++++++--------------
 2 files changed, 80 insertions(+), 40 deletions(-)

diff --git a/steve.cxx b/steve.cxx
index 37dcd69..ce3b9f8 100644
--- a/steve.cxx
+++ b/steve.cxx
@@ -397,7 +397,7 @@ static void steve_poll(
 
 static void steve_ioctl(
        fuse_req_t req, int cmd, void *, fuse_file_info *fi,
-       unsigned flags, const void *in_buf, size_t in_buf_sz, size_t out_buf_sz)
+       unsigned flags, const void *in_buf, size_t, size_t)
 {
        steve_state *state = static_cast<steve_state *>(fuse_req_userdata(req));
        /* FUSE uses the wrong type, sigh */
@@ -412,25 +412,21 @@ static void steve_ioctl(
                std::print(stderr, "PID {} requested ioctl 0x{:08x}\n",
                                fi->fh, ioctl_num);
 
-       /* TODO: is this generated by fuse? would assert() be better? */
-       if (STEVE_IOC_IS_GET(ioctl_num) && out_buf_sz != sizeof(int64_t)) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-       if (STEVE_IOC_IS_SET(ioctl_num) && in_buf_sz != sizeof(int64_t)) {
-               fuse_reply_err(req, EINVAL);
-               return;
-       }
-
        int64_t val;
+       double dval;
        if (STEVE_IOC_IS_SET(ioctl_num)) {
-               const int64_t *in_val = static_cast<const int64_t *>(in_buf);
-               if (*in_val < 0 || *in_val >= INT_MAX) {
-                       fuse_reply_err(req, EINVAL);
-                       return;
-               }
+               if (ioctl_num == STEVE_IOC_SET_LOAD_AVG) {
+                       const double *in_val = static_cast<const double 
*>(in_buf);
+                       dval = *in_val;
+               } else {
+                       const int64_t *in_val = static_cast<const int64_t 
*>(in_buf);
+                       if (*in_val < 0 || *in_val >= INT_MAX) {
+                               fuse_reply_err(req, EINVAL);
+                               return;
+                       }
 
-               val = *in_val;
+                       val = *in_val;
+               }
        }
 
        switch (ioctl_num) {
@@ -442,6 +438,10 @@ static void steve_ioctl(
                        val = state->jobs;
                        fuse_reply_ioctl(req, 0, &val, sizeof(val));
                        break;
+               case STEVE_IOC_GET_LOAD_AVG:
+                       dval = state->max_load_avg;
+                       fuse_reply_ioctl(req, 0, &dval, sizeof(dval));
+                       break;
                case STEVE_IOC_GET_MIN_JOBS:
                        val = state->min_jobs;
                        fuse_reply_ioctl(req, 0, &val, sizeof(val));
@@ -472,6 +472,16 @@ static void steve_ioctl(
                        fuse_reply_ioctl(req, 0, nullptr, 0);
                        steve_wake_waiters(state);
                        break;
+               case STEVE_IOC_SET_LOAD_AVG:
+                       if (dval < 1) {
+                               fuse_reply_err(req, EINVAL);
+                               return;
+                       }
+                       state->max_load_avg = dval;
+                       std::print("PID {} set load-average to {}\n", fi->fh, 
state->max_load_avg);
+                       fuse_reply_ioctl(req, 0, nullptr, 0);
+                       steve_wake_waiters(state);
+                       break;
                default:
                        fuse_reply_err(req, ENOTTY);
        }
@@ -566,15 +576,9 @@ int main(int argc, char **argv)
                                }
                                break;
                        case 'l':
-                               {
-                                       char *endptr;
-                                       errno = 0;
-                                       double load_avg_arg = strtod(optarg, 
&endptr);
-                                       if (*endptr || errno == ERANGE || 
load_avg_arg < 1) {
-                                               std::print(stderr, "invalid 
load average value (must be >=1): {}\n", optarg);
-                                               return 1;
-                                       }
-                                       state.max_load_avg = load_avg_arg;
+                               if (!arg_to_load_avg(optarg, 
&state.max_load_avg)) {
+                                       std::print(stderr, "invalid load 
average value (must be >=1): {}\n", optarg);
+                                       return 1;
                                }
                                break;
                        case 'v':

diff --git a/stevie.cxx b/stevie.cxx
index 2957825..3fdb95a 100644
--- a/stevie.cxx
+++ b/stevie.cxx
@@ -12,6 +12,7 @@
 #include <cstring>
 #include <print>
 #include <utility>
+#include <variant>
 #include <vector>
 
 #include <fcntl.h>
@@ -138,6 +139,9 @@ static constexpr char stevie_usage[] =
 "    --get-jobs, -j         print total job number\n"
 "    --set-jobs JOBS, -J JOBS\n"
 "                           set total job number\n"
+"    --get-load-average, -l print max load-average\n"
+"    --set-load-average LOAD_AVG, -L LOAD_AVG\n"
+"                           set max load-average\n"
 "    --get-min-jobs, -m     print min-job number\n"
 "    --set-min-jobs JOBS, -M JOBS\n"
 "                           set min-job number\n";
@@ -149,22 +153,25 @@ static const struct option stevie_long_opts[] = {
        {"get-tokens", no_argument, 0, 't'},
        {"get-jobs", no_argument, 0, 'j'},
        {"set-jobs", required_argument, 0, 'J'},
+       {"get-load-average", no_argument, 0, 'l'},
+       {"set-load-average", required_argument, 0, 'L'},
        {"get-min-jobs", no_argument, 0, 'm'},
        {"set-min-jobs", required_argument, 0, 'M'},
        {},
 };
 
-static const char *stevie_short_opts = "+hVtjJ:mM:";
+static const char *stevie_short_opts = "+hVtjJ:lL:mM:";
 
 int main(int argc, char **argv)
 {
        const char *jobserver_path = "/dev/steve";
 
        int opt;
-       std::vector<std::pair<unsigned long, int64_t>> actions;
+       std::vector<std::pair<unsigned long, std::variant<int64_t, double>>> 
actions;
        while ((opt = getopt_long(argc, argv, stevie_short_opts,
                                stevie_long_opts, nullptr)) != -1) {
                unsigned long ioctl_num = 0;
+               std::variant<int64_t, double> ioctl_val = 0;
                switch (opt) {
                case 'h':
                        std::print(stevie_usage, argv[0]);
@@ -184,6 +191,14 @@ int main(int argc, char **argv)
                case 'J':
                        ioctl_num = STEVE_IOC_SET_JOBS;
                        break;
+               case 'l':
+                       ioctl_num = STEVE_IOC_GET_LOAD_AVG;
+                       ioctl_val = 0.0;
+                       break;
+               case 'L':
+                       ioctl_num = STEVE_IOC_SET_LOAD_AVG;
+                       ioctl_val = 0.0;
+                       break;
                case 'm':
                        ioctl_num = STEVE_IOC_GET_MIN_JOBS;
                        break;
@@ -197,14 +212,23 @@ int main(int argc, char **argv)
 
                if (ioctl_num != 0) {
                        if (STEVE_IOC_IS_GET(ioctl_num)) {
-                               actions.emplace_back(ioctl_num, 0);
+                               actions.emplace_back(ioctl_num, ioctl_val);
                        } else if (STEVE_IOC_IS_SET(ioctl_num)) {
-                               long long_arg;
-                               if (!arg_to_long(optarg, &long_arg)) {
-                                       std::print(stderr, "invalid value: 
{}\n", optarg);
-                                       return 1;
-                               }
-                               actions.emplace_back(ioctl_num, long_arg);
+                               if (double *dval = 
std::get_if<double>(&ioctl_val)) {
+                                       if (!arg_to_load_avg(optarg, dval)) {
+                                               std::print(stderr, "invalid 
load average value (must be >=1): {}\n", optarg);
+                                               return 1;
+                                       }
+                               } else if 
(std::holds_alternative<int64_t>(ioctl_val)) {
+                                       long long_arg;
+                                       if (!arg_to_long(optarg, &long_arg)) {
+                                               std::print(stderr, "invalid 
value: {}\n", optarg);
+                                               return 1;
+                                       }
+                                       ioctl_val = long_arg;
+                               } else
+                                       assert(0 && "not reached");
+                               actions.emplace_back(ioctl_num, ioctl_val);
                        } else
                                assert(0 && "not reached");
                }
@@ -224,16 +248,28 @@ int main(int argc, char **argv)
 
        for (auto &action : actions) {
                unsigned long ioctl_num = action.first;
-               int64_t ioctl_val = action.second;
+               int64_t *ioctl_val = std::get_if<int64_t>(&action.second);
+               double *ioctl_dval = std::get_if<double>(&action.second);
+
+               int ret = -1;
+               if (ioctl_val)
+                       ret = ioctl(jobserver_fd, ioctl_num, ioctl_val);
+               else if (ioctl_dval)
+                       ret = ioctl(jobserver_fd, ioctl_num, ioctl_dval);
+               else
+                       assert(0 && "not reached");
 
-               if (ioctl(jobserver_fd, ioctl_num, &ioctl_val) != 0) {
+               if (ret != 0) {
                        perror("ioctl failed");
                        return 1;
                }
 
-               if (STEVE_IOC_IS_GET(ioctl_num))
-                       std::print("{}\n", ioctl_val);
-               else if (STEVE_IOC_IS_SET(ioctl_num))
+               if (STEVE_IOC_IS_GET(ioctl_num)) {
+                       if (ioctl_val)
+                               std::print("{}\n", *ioctl_val);
+                       else if (ioctl_dval)
+                               std::print("{}\n", *ioctl_dval);
+               } else if (STEVE_IOC_IS_SET(ioctl_num))
                        std::print("ok\n");
                else
                        assert(0 && "not reached");

Reply via email to