| /*****************************************************************************\ |
| * allocate_msg.c - Message handler for communication with with |
| * the slurmctld during an allocation. |
| ***************************************************************************** |
| * Copyright (C) 2002 The Regents of the University of California. |
| * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). |
| * Written by Morris Jette <jette1@llnl.gov>. |
| * CODE-OCEC-09-009. All rights reserved. |
| * |
| * This file is part of Slurm, a resource management program. |
| * For details, see <https://slurm.schedmd.com/>. |
| * Please also read the included file: DISCLAIMER. |
| * |
| * Slurm is free software; you can redistribute it and/or modify it under |
| * the terms of the GNU General Public License as published by the Free |
| * Software Foundation; either version 2 of the License, or (at your option) |
| * any later version. |
| * |
| * In addition, as a special exception, the copyright holders give permission |
| * to link the code of portions of this program with the OpenSSL library under |
| * certain conditions as described in each individual source file, and |
| * distribute linked combinations including the two. You must obey the GNU |
| * General Public License in all respects for all of the code used other than |
| * OpenSSL. If you modify file(s) with this exception, you may extend this |
| * exception to your version of the file(s), but you are not obligated to do |
| * so. If you do not wish to do so, delete this exception statement from your |
| * version. If you delete this exception statement from all source files in |
| * the program, then also delete it here. |
| * |
| * Slurm is distributed in the hope that it will be useful, but WITHOUT ANY |
| * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
| * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
| * details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with Slurm; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| \*****************************************************************************/ |
| |
| #include <sys/un.h> |
| |
| #include "slurm/slurm.h" |
| |
| #include "src/common/duplex_relay.h" |
| #include "src/common/events.h" |
| #include "src/common/macros.h" |
| #include "src/common/net.h" |
| #include "src/common/read_config.h" |
| #include "src/common/slurm_protocol_api.h" |
| #include "src/common/xmalloc.h" |
| |
| #define MAGIC_X11_CON 0xba59504c |
| |
| typedef struct { |
| int magic; /* MAGIC_X11_CON */ |
| conmgr_fd_ref_t *con; |
| slurm_msg_t *resp_msg; |
| } x11_con_t; |
| |
| typedef struct { |
| void (*func)(slurm_allocation_callbacks_t *callbacks, slurm_msg_t *msg); |
| uint16_t msg_type; |
| } alloc_rpc_t; |
| |
| static void _handle_ping(slurm_allocation_callbacks_t *callbacks, |
| slurm_msg_t *msg) |
| { |
| debug3("received ping message"); |
| slurm_send_rc_msg(msg, SLURM_SUCCESS); |
| } |
| |
| static void _handle_job_complete(slurm_allocation_callbacks_t *callbacks, |
| slurm_msg_t *msg) |
| { |
| srun_job_complete_msg_t *comp = msg->data; |
| debug3("job complete message received"); |
| |
| if (callbacks->job_complete != NULL) |
| (callbacks->job_complete)(comp); |
| } |
| |
| /* |
| * Job has been notified of it's approaching time limit. |
| * Job will be killed shortly after timeout. |
| * This RPC can arrive multiple times with the same or updated timeouts. |
| */ |
| static void _handle_timeout(slurm_allocation_callbacks_t *callbacks, |
| slurm_msg_t *msg) |
| { |
| srun_timeout_msg_t *to = msg->data; |
| |
| debug3("received timeout message"); |
| |
| if (callbacks->timeout != NULL) |
| (callbacks->timeout)(to); |
| } |
| |
| static void _handle_user_msg(slurm_allocation_callbacks_t *callbacks, |
| slurm_msg_t *msg) |
| { |
| srun_user_msg_t *um = msg->data; |
| debug3("received user message"); |
| |
| if (callbacks->user_msg != NULL) |
| (callbacks->user_msg)(um); |
| } |
| |
| static void _handle_node_fail(slurm_allocation_callbacks_t *callbacks, |
| slurm_msg_t *msg) |
| { |
| srun_node_fail_msg_t *nf = msg->data; |
| |
| if (callbacks->node_fail != NULL) |
| (callbacks->node_fail)(nf); |
| } |
| |
| static void _handle_suspend(slurm_allocation_callbacks_t *callbacks, |
| slurm_msg_t *msg) |
| { |
| suspend_msg_t *sus_msg = msg->data; |
| debug3("received suspend message"); |
| |
| if (callbacks->job_suspend != NULL) |
| (callbacks->job_suspend)(sus_msg); |
| } |
| |
| static void _x11_con_free(x11_con_t *x11con) |
| { |
| if (!x11con) |
| return; |
| |
| xassert(x11con->magic == MAGIC_X11_CON); |
| x11con->magic = ~MAGIC_X11_CON; |
| conmgr_con_queue_close_free(&x11con->con); |
| FREE_NULL_MSG(x11con->resp_msg); |
| xfree(x11con); |
| } |
| |
| static void _x11_con_duplex_relay_assign(conmgr_callback_args_t conmgr_args, |
| void *arg) |
| { |
| x11_con_t *x11con = arg; |
| conmgr_fd_ref_t *con = conmgr_args.ref; |
| int rc = SLURM_ERROR; |
| |
| xassert(x11con->magic == MAGIC_X11_CON); |
| if ((rc = duplex_relay_assign(con, x11con->con))) { |
| error("%s: [%s] Failed to initialize second connection in duplex relay", |
| __func__, conmgr_con_get_name(con)); |
| /* |
| * duplex_relay_assign() failed before reassigning the |
| * connection's events, so con still references x11con through |
| * its arg and the _x11_con_on_finish() callback. Close con and |
| * let _x11_con_on_finish() free x11con to avoid a |
| * use-after-free. |
| */ |
| conmgr_con_queue_close(con); |
| return; |
| } |
| |
| log_flag(NET, "%s: [%s] Opened and initialized connection to local x11 server, X11 tunnel is ready now", |
| __func__, conmgr_con_get_name(con)); |
| |
| /* Cleanup state but avoid closing the connections */ |
| CONMGR_CON_UNLINK(x11con->con); |
| |
| _x11_con_free(x11con); |
| } |
| |
| static void *_x11_con_on_connection(conmgr_callback_args_t conmgr_args, |
| void *arg) |
| { |
| x11_con_t *x11con = arg; |
| int rc; |
| |
| if ((rc = conmgr_con_queue_write_msg(x11con->con, x11con->resp_msg))) { |
| error("%s: [%s] Failed to write SLURM_SUCCESS back to slurmstepd: %s", |
| __func__, conmgr_con_get_name(conmgr_args.ref), |
| slurm_strerror(rc)); |
| _x11_con_free(x11con); |
| return NULL; |
| } |
| |
| conmgr_add_work_con_fifo(conmgr_args.con, _x11_con_duplex_relay_assign, |
| arg); |
| |
| return arg; |
| } |
| |
| static void _x11_con_on_finish(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| x11_con_t *x11con = arg; |
| |
| if (!x11con) |
| return; |
| |
| xassert(x11con->magic == MAGIC_X11_CON); |
| |
| _x11_con_free(x11con); |
| } |
| |
| static int _x11_con_on_data(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| /* |
| * This should never be called, connection should be quiesced until |
| * on_data() is reassigned by duplex_relay. |
| */ |
| xassert(false); |
| |
| return SLURM_ERROR; |
| } |
| |
| static void _handle_net_forward(slurm_allocation_callbacks_t *callbacks, |
| slurm_msg_t *msg) |
| { |
| conmgr_fd_ref_t *con = msg->conmgr_con; |
| net_forward_msg_t *forward_msg = msg->data; |
| slurm_addr_t local_x11_server_addr = { 0 }; |
| socklen_t addrlen = sizeof(local_x11_server_addr); |
| static const conmgr_events_t events = { |
| .on_connection = _x11_con_on_connection, |
| .on_data = _x11_con_on_data, |
| .on_finish = _x11_con_on_finish, |
| }; |
| int rc; |
| x11_con_t *x11con = NULL; |
| return_code_msg_t *success_rc_msg; |
| |
| if (forward_msg->port) { |
| slurm_set_addr(&local_x11_server_addr, forward_msg->port, |
| forward_msg->target); |
| } else if (forward_msg->target) { |
| local_x11_server_addr = |
| sockaddr_from_unix_path(forward_msg->target); |
| addrlen = sizeof(struct sockaddr_un); |
| } |
| |
| x11con = xmalloc(sizeof(*x11con)); |
| *x11con = (x11_con_t) { |
| .magic = MAGIC_X11_CON, |
| .resp_msg = xmalloc(sizeof(*x11con->resp_msg)), |
| }; |
| |
| CONMGR_CON_LINK(con, x11con->con); |
| |
| /* |
| * Prepare SLURM_SUCCESS response message now to be sent later in |
| * _x11_con_on_connection(). |
| */ |
| success_rc_msg = xmalloc(sizeof(*success_rc_msg)); |
| success_rc_msg->return_code = SLURM_SUCCESS; |
| slurm_resp_msg_init(x11con->resp_msg, msg, RESPONSE_SLURM_RC, |
| success_rc_msg); |
| |
| if ((rc = conmgr_create_connect_socket(CON_TYPE_RAW, CON_FLAG_QUIESCE, |
| &local_x11_server_addr, addrlen, |
| &events, NULL, x11con))) { |
| error("Failed to connect to local X11 server at '%pA': %s", |
| &local_x11_server_addr, slurm_strerror(rc)); |
| /* |
| * Send the error rc to slurmstepd before _x11_con_free() closes |
| * the shared connection, otherwise the queued close may tear it |
| * down before the response is flushed. |
| */ |
| slurm_send_rc_msg(msg, SLURM_ERROR); |
| _x11_con_free(x11con); |
| return; |
| } |
| |
| /* |
| * _x11_con_on_connection() will send SLURM_SUCCESS back, this ensures |
| * that it sends the rc back *before* starting duplex_relay. |
| */ |
| log_flag(NET, "%s: [%s] Connected to local X11 server, will send SLURM_SUCCESS back to slurmstepd to continue setting up X11 tunnel", |
| __func__, conmgr_con_get_name(con)); |
| |
| /* |
| * make sure _on_msg() doesn't close the connection as it will stay |
| * alive for the duplex relay. |
| */ |
| CONMGR_CON_UNLINK(msg->conmgr_con); |
| } |
| |
| static alloc_rpc_t alloc_rpcs[] = { |
| { |
| .msg_type = SRUN_PING, |
| .func = _handle_ping, |
| }, |
| { |
| .msg_type = SRUN_JOB_COMPLETE, |
| .func = _handle_job_complete, |
| }, |
| { |
| .msg_type = SRUN_TIMEOUT, |
| .func = _handle_timeout, |
| }, |
| { |
| .msg_type = SRUN_USER_MSG, |
| .func = _handle_user_msg, |
| }, |
| { |
| .msg_type = SRUN_NODE_FAIL, |
| .func = _handle_node_fail, |
| }, |
| { |
| .msg_type = SRUN_REQUEST_SUSPEND, |
| .func = _handle_suspend, |
| }, |
| { |
| .msg_type = SRUN_NET_FORWARD, |
| .func = _handle_net_forward, |
| }, |
| { |
| /* terminate the array. this must be last. */ |
| .msg_type = 0, |
| .func = NULL, |
| } |
| }; |
| |
| static void *_on_connection(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_ref_t *con = conmgr_args.ref; |
| |
| log_flag(NET, "%s: [%s] New connection accepted by alloc_msg listener", |
| __func__, conmgr_con_get_name(con)); |
| |
| return arg; |
| } |
| |
| static void _on_finish(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_ref_t *con = conmgr_args.ref; |
| |
| log_flag(NET, "%s: [%s] connection finished", |
| __func__, conmgr_con_get_name(con)); |
| } |
| |
| static int _on_msg(conmgr_callback_args_t conmgr_args, slurm_msg_t *msg, |
| int unpack_rc, void *arg) |
| { |
| slurm_allocation_callbacks_t *callbacks = arg; |
| conmgr_fd_ref_t *con = conmgr_args.ref; |
| uid_t uid = getuid(); |
| alloc_rpc_t *this_rpc = NULL; |
| int rc = SLURM_SUCCESS; |
| |
| if (!msg->auth_ids_set) { |
| error("%s: [%s] Security violation, rejecting unauthenticated slurm message", |
| __func__, conmgr_con_get_name(con)); |
| rc = SLURM_PROTOCOL_AUTHENTICATION_ERROR; |
| goto end; |
| } |
| |
| log_flag(AUDIT_RPCS, "allocate_msg _on_msg: [%s] msg_type=%s uid=%u client=[%pA] protocol=%u", |
| conmgr_con_get_name(con), rpc_num2string(msg->msg_type), |
| msg->auth_uid, &msg->address, msg->protocol_version); |
| |
| if (unpack_rc) { |
| error("%s: [%s] rejecting malformed RPC and closing connection: %s", |
| __func__, conmgr_con_get_name(con), |
| slurm_strerror(unpack_rc)); |
| rc = unpack_rc; |
| goto end; |
| } |
| |
| if ((msg->auth_uid != slurm_conf.slurm_user_id) && |
| (msg->auth_uid != 0) && (msg->auth_uid != uid)) { |
| error ("Security violation, slurm message from uid %u", |
| msg->auth_uid); |
| rc = SLURM_PROTOCOL_AUTHENTICATION_ERROR; |
| goto end; |
| } |
| |
| for (this_rpc = alloc_rpcs; this_rpc->msg_type; this_rpc++) { |
| if (this_rpc->msg_type == msg->msg_type) |
| break; |
| } |
| |
| if (!this_rpc->msg_type) { |
| error("%s: received spurious message type: %s", |
| __func__, rpc_num2string(msg->msg_type)); |
| goto end; |
| } |
| |
| this_rpc->func(callbacks, msg); |
| |
| end: |
| conmgr_con_queue_close(msg->conmgr_con); |
| FREE_NULL_MSG(msg); |
| return rc; |
| } |
| |
| static void *_on_listen_connect(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_ref_t *con = conmgr_args.ref; |
| |
| log_flag(NET, "%s: [%s] alloc_msg listener now open", |
| __func__, conmgr_con_get_name(con)); |
| |
| return arg; |
| } |
| |
| static void _on_listen_finish(conmgr_callback_args_t conmgr_args, void *arg) |
| { |
| conmgr_fd_ref_t *con = conmgr_args.ref; |
| |
| log_flag(NET, "%s: [%s] alloc_msg listener connection finished", |
| __func__, conmgr_con_get_name(con)); |
| } |
| |
| extern int slurm_alloc_msg_listener_create(uint16_t *port, |
| slurm_allocation_callbacks_t |
| *callbacks) |
| { |
| int listen_fd = -1; |
| static const conmgr_events_t events = { |
| .on_listen_connect = _on_listen_connect, |
| .on_listen_finish = _on_listen_finish, |
| .on_connection = _on_connection, |
| .on_msg = _on_msg, |
| .on_finish = _on_finish, |
| }; |
| conmgr_con_flags_t flags = CON_FLAG_NONE; |
| int rc = SLURM_SUCCESS; |
| |
| if (conn_tls_enabled()) |
| flags |= CON_FLAG_TLS_FINGERPRINT; |
| |
| if (slurm_init_msg_engine_srun_ports(&listen_fd, port) != |
| SLURM_SUCCESS) { |
| fatal("Unable to open listening socket for messages"); |
| } |
| if ((rc = conmgr_process_fd_listen(listen_fd, CON_TYPE_RPC, NULL, |
| &events, flags, callbacks))) { |
| fatal("%s: unable to process fd:%d error:%s", |
| __func__, listen_fd, slurm_strerror(rc)); |
| } |
| |
| return rc; |
| } |