From 57c22148e00d048c46174ee560f8a88561bd6d45 Mon Sep 17 00:00:00 2001 From: Donald Buczek Date: Wed, 27 Dec 2023 15:07:50 +0100 Subject: [PATCH] mxqkill: Add cancel_job To cancel a single job: - Verify the owner uid in advance, so that we don't need a join with mxq_group during a later update statement. The join during update doesn't work well with the triggers, which want to modify mxq_group. By avoiding this, we can later dispose the CANCELLING state which is also just a workaround. - Set job_cancelled to true for the job (don't mind job_status) job_status transitions will be done by following commits. --- mxqkill.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/mxqkill.c b/mxqkill.c index 667122a9..1989c7ab 100644 --- a/mxqkill.c +++ b/mxqkill.c @@ -33,6 +33,16 @@ #define UINT64_SPECIAL_MIN (uint64_t)(-2) #define UINT64_HASVALUE(x) ((x) < UINT64_SPECIAL_MIN) +__attribute__ ((noreturn)) +__attribute__ ((format (printf, 1, 2))) +static void die(const char *restrict fmt, ...) { + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + _exit(1); +} + static void print_usage(void) { mxq_print_generic_version(); @@ -148,6 +158,65 @@ static int update_job_status_cancelled_by_group(struct mx_mysql *mysql, struct m return (int)num_rows; } +static void verify_job_owner(struct mx_mysql *mysql, uint64_t job_id, uint64_t user_uid) { + struct mx_mysql_stmt *stmt = mx_mysql_statement_prepare(mysql, + "SELECT user_uid FROM mxq_job, mxq_group" + " WHERE mxq_job.group_id = mxq_group.group_id" + " AND job_id = ?" + ); + if (!stmt) + die("mx_mysql_statement_prepare(): %s\n", mx_mysql_error()); + + mx_mysql_statement_param_bind(stmt, 0, uint64, &job_id); + + unsigned long long num_rows; + int res = mx_mysql_statement_execute(stmt, &num_rows); + if (res < 0) + die("mx_mysql_statement_execute(): %s\n", mx_mysql_error()); + + if (num_rows == 0) + die("no such job_id %lu\n", job_id); + + uint64_t uid; + mx_mysql_statement_result_bind(stmt, 0, uint64, &uid); + + res = mx_mysql_statement_fetch(stmt); + if (res < 0) + die("mx_mysql_statement_fetch: %s\n", mx_mysql_error()); + + if (uid != user_uid) + die("job %lu: permission denied\n", job_id); + + mx_mysql_statement_close(&stmt); +} + +static void set_job_cancelled(struct mx_mysql *mysql, uint64_t job_id) { + struct mx_mysql_stmt *stmt = mx_mysql_statement_prepare(mysql, + "UPDATE mxq_job SET job_cancelled = TRUE" + " WHERE job_id = ?" + ); + if (!stmt) + die("mx_mysql_statement_prepare(): %s\n", mx_mysql_error()); + + mx_mysql_statement_param_bind(stmt, 0, uint64, &job_id); + + unsigned long long num_rows; + int res = mx_mysql_statement_execute(stmt, &num_rows); + if (res < 0) + die("mx_mysql_statement_execute(): %s\n", mx_mysql_error()); + if (num_rows == 0) + die("no such job_id %lu\n", job_id); + + mx_mysql_statement_close(&stmt); +} + +__attribute__ ((unused)) +static void cancel_job(struct mx_mysql *mysql, uint64_t job_id, uint64_t user_uid) { + + verify_job_owner(mysql, job_id, user_uid); + set_job_cancelled(mysql, job_id); +} + static int update_job_status_cancelling_by_job_id_for_user(struct mx_mysql *mysql, uint64_t job_id, uint64_t user_uid) { struct mx_mysql_stmt *stmt = NULL;