From 9d8c96d7adb8a22d1f5d052a0976a213d356859e Mon Sep 17 00:00:00 2001
From: David Bigagli <david@schedmd.com>
Date: Wed, 4 Sep 2013 13:25:14 -0700
Subject: [PATCH] Add 'show licenses' option to scontrol command.

---
 NEWS                             |   2 +
 RELEASE_NOTES                    |   8 +-
 slurm/slurm.h.in                 |  28 +++++++
 src/api/Makefile.am              |   4 +-
 src/api/Makefile.in              |   6 +-
 src/api/partition_info.c         |   6 +-
 src/common/slurm_protocol_defs.c |  24 ++++++
 src/common/slurm_protocol_defs.h |   9 +++
 src/common/slurm_protocol_pack.c | 126 ++++++++++++++++++++++++++++++-
 src/scontrol/Makefile.am         |   3 +-
 src/scontrol/Makefile.in         |   6 +-
 src/scontrol/scontrol.c          |   6 +-
 src/scontrol/scontrol.h          |   2 +-
 src/slurmctld/licenses.c         |  91 ++++++++++++++++++++--
 src/slurmctld/licenses.h         |  11 +++
 src/slurmctld/proc_req.c         |  48 +++++++++++-
 16 files changed, 357 insertions(+), 23 deletions(-)

diff --git a/NEWS b/NEWS
index 2a00ff3687e..11022317b05 100644
--- a/NEWS
+++ b/NEWS
@@ -22,6 +22,8 @@ documents those changes that are of interest to users and admins.
     slurmstepd.
  -- CRAY - Do not package Slurm's libpmi or libpmi2 libraries. The Cray version
     of those libraries must be used.
+ -- Added a new option to the scontrol command to view licenses that are configured
+    in use and avalable. 'scontrol show licenses'.
 
 * Changes in Slurm 13.12.0pre1
 ==============================
diff --git a/RELEASE_NOTES b/RELEASE_NOTES
index 42b87308b4c..2fc4e10443c 100644
--- a/RELEASE_NOTES
+++ b/RELEASE_NOTES
@@ -77,6 +77,8 @@ COMMAND CHANGES (see man pages for details)
  -- Add squeue output format options for job command and working directory
     (%o and %Z respectively).
  -- Add stdin/out/err to sview job output.
+ -- Added a new option to the scontrol command to view licenses that are configured
+    in use and avalable. 'scontrol show licenses'.
 
 OTHER CHANGES
 =============
@@ -104,8 +106,12 @@ Changed the following enums and #defines
 Added the following API's
 =========================
 
+-- API for retrieving license information.
 
-Changed the following API's
+extern int slurm_load_licenses PARAMS((license_info_msg_t **));
+extern void slurm_free_license_info_msg PARAMS((license_info_msg_t *));
+
+Change the following API's
 ===========================
  -- Add task pointer to the task_post_term() function in task plugins. The
     terminating task's PID is available in task->pid.
diff --git a/slurm/slurm.h.in b/slurm/slurm.h.in
index 2d1eeaa32b9..4776aaaea69 100644
--- a/slurm/slurm.h.in
+++ b/slurm/slurm.h.in
@@ -2352,6 +2352,24 @@ typedef struct trigger_info_msg {
 	trigger_info_t *trigger_array;	/* the trigger records */
 } trigger_info_msg_t;
 
+
+/* Individual license information
+ */
+typedef struct slurm_license_info {
+	char *feature;       /* feature name */
+	uint32_t total;      /* total number of available licenses */
+	uint32_t in_use;     /* number of license in use */
+	uint32_t available;  /* number of available license */
+} slurm_license_info_t;
+
+/* License information array as returned by the controller.
+ */
+typedef struct license_info_msg {
+	time_t last_update;
+	uint32_t num_features;
+	slurm_license_info_t *lic_array;
+} license_info_msg_t;
+
 /*****************************************************************************\
  *	RESOURCE ALLOCATION FUNCTIONS
 \*****************************************************************************/
@@ -2518,6 +2536,16 @@ extern int slurm_sbcast_lookup PARAMS((uint32_t jobid,
 
 extern void slurm_free_sbcast_cred_msg PARAMS((job_sbcast_cred_msg_t * msg));
 
+/* slurm_load_licenses()
+ *
+ * Retrieve license information from the controller.
+ * IN feature - feature name or NULL
+ * OUT
+ *
+ */
+extern int slurm_load_licenses PARAMS((license_info_msg_t **));
+extern void slurm_free_license_info_msg PARAMS((license_info_msg_t *));
+
 /*****************************************************************************\
  *	JOB/STEP SIGNALING FUNCTIONS
 \*****************************************************************************/
diff --git a/src/api/Makefile.am b/src/api/Makefile.am
index f5ef0ff0064..c4f1e7bccb0 100644
--- a/src/api/Makefile.am
+++ b/src/api/Makefile.am
@@ -106,8 +106,8 @@ slurmapi_src =           \
 	topo_info.c      \
 	triggers.c       \
 	reconfigure.c    \
-	update_config.c
-
+	update_config.c  \
+	license_info.c
 
 common_dir = $(top_builddir)/src/common
 
diff --git a/src/api/Makefile.in b/src/api/Makefile.in
index dcf7a7c57cb..fe332880f73 100644
--- a/src/api/Makefile.in
+++ b/src/api/Makefile.in
@@ -157,7 +157,7 @@ am__objects_1 = allocate.lo allocate_msg.lo block_info.lo cancel.lo \
 	slurm_get_statistics.lo slurm_hostlist.lo slurm_pmi.lo \
 	step_ctx.lo step_io.lo step_launch.lo pmi_server.lo submit.lo \
 	suspend.lo topo_info.lo triggers.lo reconfigure.lo \
-	update_config.lo
+	update_config.lo license_info.lo
 am_libslurmhelper_la_OBJECTS = $(am__objects_1)
 libslurmhelper_la_OBJECTS = $(am_libslurmhelper_la_OBJECTS)
 PROGRAMS = $(noinst_PROGRAMS)
@@ -523,7 +523,8 @@ slurmapi_src = \
 	topo_info.c      \
 	triggers.c       \
 	reconfigure.c    \
-	update_config.c
+	update_config.c  \
+	license_info.c
 
 common_dir = $(top_builddir)/src/common
 slurmapi_add = \
@@ -683,6 +684,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_msg.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job_info.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/job_step_info.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/license_info.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/node_info.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/partition_info.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pmi.Plo@am__quote@
diff --git a/src/api/partition_info.c b/src/api/partition_info.c
index 259b721e1ee..aa28e040d3e 100644
--- a/src/api/partition_info.c
+++ b/src/api/partition_info.c
@@ -439,10 +439,10 @@ extern int slurm_load_partitions (time_t update_time,
 	slurm_msg_t_init(&req_msg);
 	slurm_msg_t_init(&resp_msg);
 
-        req.last_update  = update_time;
+	req.last_update  = update_time;
 	req.show_flags   = show_flags;
-        req_msg.msg_type = REQUEST_PARTITION_INFO;
-        req_msg.data     = &req;
+	req_msg.msg_type = REQUEST_PARTITION_INFO;
+	req_msg.data     = &req;
 
 	if (slurm_send_recv_controller_msg(&req_msg, &resp_msg) < 0)
 		return SLURM_ERROR;
diff --git a/src/common/slurm_protocol_defs.c b/src/common/slurm_protocol_defs.c
index ae46df31134..bb960facdab 100644
--- a/src/common/slurm_protocol_defs.c
+++ b/src/common/slurm_protocol_defs.c
@@ -2877,3 +2877,27 @@ extern char *slurm_ctime_r(const time_t *timep, char *time_str)
 
 	return time_str;
 }
+
+/* slurm_free_license_info()
+ *
+ * Free the license info returned previously
+ * from the controller.
+ */
+extern void
+slurm_free_license_info_msg(license_info_msg_t *msg)
+{
+	int cc;
+
+	if (msg == NULL)
+		return;
+
+	for (cc = 0; cc < msg->num_features; cc++) {
+		xfree(msg->lic_array[cc].feature);
+	}
+	xfree(msg->lic_array);
+	xfree(msg);
+}
+extern void slurm_free_license_info_request_msg(license_info_request_msg_t *msg)
+{
+	xfree(msg);
+}
diff --git a/src/common/slurm_protocol_defs.h b/src/common/slurm_protocol_defs.h
index 87018da6337..28d12500a1b 100644
--- a/src/common/slurm_protocol_defs.h
+++ b/src/common/slurm_protocol_defs.h
@@ -196,6 +196,8 @@ typedef enum {
 	RESPONSE_ACCT_GATHER_UPDATE,
 	REQUEST_ACCT_GATHER_ENERGY,
 	RESPONSE_ACCT_GATHER_ENERGY,
+	REQUEST_LICENSE_INFO,
+	RESPONSE_LICENSE_INFO,
 
 	REQUEST_BUILD_INFO = 2001,
 	RESPONSE_BUILD_INFO,
@@ -971,6 +973,11 @@ typedef struct ping_slurmd_resp_msg {
 	uint32_t cpu_load;	/* CPU load * 100 */
 } ping_slurmd_resp_msg_t;
 
+typedef struct license_info_request_msg {
+	time_t last_update;
+	uint16_t show_flags;
+} license_info_request_msg_t;
+
 /*****************************************************************************\
  * Slurm API Message Types
 \*****************************************************************************/
@@ -1194,6 +1201,8 @@ extern void slurm_free_spank_env_request_msg(spank_env_request_msg_t *msg);
 extern void slurm_free_spank_env_responce_msg(spank_env_responce_msg_t *msg);
 
 extern int slurm_free_msg_data(slurm_msg_type_t type, void *data);
+extern void slurm_free_license_info_request_msg(license_info_request_msg_t *msg);
+
 extern uint32_t slurm_get_return_code(slurm_msg_type_t type, void *data);
 
 extern char *preempt_mode_string(uint16_t preempt_mode);
diff --git a/src/common/slurm_protocol_pack.c b/src/common/slurm_protocol_pack.c
index a3aea63e7d0..8618965f1c2 100644
--- a/src/common/slurm_protocol_pack.c
+++ b/src/common/slurm_protocol_pack.c
@@ -645,6 +645,16 @@ static void _pack_ping_slurmd_resp(ping_slurmd_resp_msg_t *msg,
 static int _unpack_ping_slurmd_resp(ping_slurmd_resp_msg_t **msg_ptr,
 				    Buf buffer, uint16_t protocol_version);
 
+static void _pack_license_info_request_msg(license_info_request_msg_t *msg,
+                                           Buf buffer,
+                                           uint16_t protocol_version);
+static int _unpack_license_info_request_msg(license_info_request_msg_t **msg,
+                                            Buf buffer,
+                                            uint16_t protocol_version);
+static inline void _pack_license_info_msg(slurm_msg_t *msg, Buf buffer);
+static int _unpack_license_info_msg(license_info_msg_t **msg,
+                                    Buf buffer,
+                                    uint16_t protocol_version);
 /* pack_header
  * packs a slurm protocol header that precedes every slurm message
  * IN header - the header structure to pack
@@ -1261,7 +1271,15 @@ pack_msg(slurm_msg_t const *msg, Buf buffer)
 		_pack_ping_slurmd_resp((ping_slurmd_resp_msg_t *)msg->data,
 				       buffer, msg->protocol_version);
 		break;
-
+	case REQUEST_LICENSE_INFO:
+		 _pack_license_info_request_msg((license_info_request_msg_t *)
+		                                msg->data,
+		                                buffer,
+		                                msg->protocol_version);
+			break;
+	case RESPONSE_LICENSE_INFO:
+		_pack_license_info_msg((slurm_msg_t *) msg, buffer);
+		break;
 	default:
 		debug("No pack method for msg type %u", msg->msg_type);
 		return EINVAL;
@@ -1869,7 +1887,17 @@ unpack_msg(slurm_msg_t * msg, Buf buffer)
 					      &msg->data, buffer,
 					      msg->protocol_version);
 		break;
-
+	case RESPONSE_LICENSE_INFO:
+		rc = _unpack_license_info_msg((license_info_msg_t **)&(msg->data),
+		                              buffer,
+		                              msg->protocol_version);
+		break;
+	case REQUEST_LICENSE_INFO:
+		rc = _unpack_license_info_request_msg((license_info_request_msg_t **)
+		                                      &(msg->data),
+		                                      buffer,
+		                                      msg->protocol_version);
+		break;
 	default:
 		debug("No unpack method for msg type %u", msg->msg_type);
 		return EINVAL;
@@ -10704,6 +10732,100 @@ unpack_error:
 	return SLURM_ERROR;
 }
 
+/* _pack_license_info_request_msg()
+ */
+static void
+_pack_license_info_request_msg(license_info_request_msg_t *msg,
+                               Buf buffer,
+                               uint16_t protocol_version)
+{
+	pack_time(msg->last_update, buffer);
+	pack16((uint16_t)msg->show_flags, buffer);
+}
+
+/* _unpack_license_info_request_msg()
+ */
+static int
+_unpack_license_info_request_msg(license_info_request_msg_t **msg,
+                                 Buf buffer,
+                                 uint16_t protocol_version)
+{
+	*msg = xmalloc(sizeof(license_info_msg_t));
+
+	safe_unpack_time(&(*msg)->last_update, buffer);
+	safe_unpack16(&(*msg)->show_flags, buffer);
+
+	return SLURM_SUCCESS;
+
+unpack_error:
+	slurm_free_license_info_request_msg(*msg);
+	*msg = NULL;
+	return SLURM_ERROR;
+}
+
+/* _pack_license_info_msg()
+ */
+static inline void
+_pack_license_info_msg(slurm_msg_t *msg, Buf buffer)
+{
+	_pack_buffer_msg(msg, buffer);
+}
+
+/* _unpack_license_info_msg()
+ *
+ * Decode the array of license as it comes from the
+ * controller and build the API licenses structures
+ * as defined in slurm.h
+ *
+ */
+static int
+_unpack_license_info_msg(license_info_msg_t **msg,
+                         Buf buffer,
+                         uint16_t protocol_version)
+{
+	int i;
+	uint32_t zz;
+
+	xassert(msg != NULL);
+	*msg = xmalloc(sizeof(license_info_msg_t));
+
+	/* load buffer's header (data structure version and time)
+	 */
+	if (protocol_version >= SLURM_13_12_PROTOCOL_VERSION) {
+
+		safe_unpack32(&((*msg)->num_features), buffer);
+		safe_unpack_time(&((*msg)->last_update), buffer);
+
+		(*msg)->lic_array = xmalloc(sizeof(slurm_license_info_t)
+		                            * (*msg)->num_features);
+
+		/* Decode individual license data.
+		 */
+		for (i = 0; i < (*msg)->num_features; i++) {
+
+			safe_unpackstr_xmalloc(&((*msg)->lic_array[i]).feature, &zz, buffer);
+			safe_unpack32(&((*msg)->lic_array[i]).total, buffer);
+			safe_unpack32(&((*msg)->lic_array[i]).in_use, buffer);
+			(*msg)->lic_array[i].available
+				= (*msg)->lic_array[i].total - (*msg)->lic_array[i].in_use;
+			xassert((*msg)->lic_array[i].available >= 0);
+		}
+
+	} else {
+		error("_unpack_partition_info_msg: protocol_version "
+		      "%hu not supported", protocol_version);
+		goto unpack_error;
+	}
+
+	return SLURM_SUCCESS;
+
+unpack_error:
+	slurm_free_license_info_msg(*msg);
+	*msg = NULL;
+	return SLURM_ERROR;
+}
+
+
 /* template
    void pack_ ( * msg , Buf buffer )
    {
diff --git a/src/scontrol/Makefile.am b/src/scontrol/Makefile.am
index 22e0132a6b1..49f545c672b 100644
--- a/src/scontrol/Makefile.am
+++ b/src/scontrol/Makefile.am
@@ -18,7 +18,8 @@ scontrol_SOURCES =	\
 	update_job.c	\
 	update_node.c	\
 	update_part.c	\
-	update_step.c
+	update_step.c   \
+	info_lics.c
 
 convenience_libs = $(top_builddir)/src/api/libslurm.o $(DL_LIBS) -lm
 
diff --git a/src/scontrol/Makefile.in b/src/scontrol/Makefile.in
index d0346ed57da..3e50bd35112 100644
--- a/src/scontrol/Makefile.in
+++ b/src/scontrol/Makefile.in
@@ -110,7 +110,7 @@ am_scontrol_OBJECTS = create_res.$(OBJEXT) info_block.$(OBJEXT) \
 	info_job.$(OBJEXT) info_node.$(OBJEXT) info_part.$(OBJEXT) \
 	info_res.$(OBJEXT) scontrol.$(OBJEXT) update_job.$(OBJEXT) \
 	update_node.$(OBJEXT) update_part.$(OBJEXT) \
-	update_step.$(OBJEXT)
+	update_step.$(OBJEXT) info_lics.$(OBJEXT)
 scontrol_OBJECTS = $(am_scontrol_OBJECTS)
 am__DEPENDENCIES_1 =
 am__DEPENDENCIES_2 = $(top_builddir)/src/api/libslurm.o \
@@ -387,7 +387,8 @@ scontrol_SOURCES = \
 	update_job.c	\
 	update_node.c	\
 	update_part.c	\
-	update_step.c
+	update_step.c   \
+	info_lics.c
 
 convenience_libs = $(top_builddir)/src/api/libslurm.o $(DL_LIBS) -lm
 scontrol_LDADD = \
@@ -490,6 +491,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create_res.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_block.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_job.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_lics.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_node.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_part.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info_res.Po@am__quote@
diff --git a/src/scontrol/scontrol.c b/src/scontrol/scontrol.c
index 4526ae9f6c5..2c3b40a92db 100644
--- a/src/scontrol/scontrol.c
+++ b/src/scontrol/scontrol.c
@@ -1207,8 +1207,7 @@ _process_command (int argc, char *argv[])
 			exit_code = 1;
 			slurm_perror("job notify failure");
 		}
-	}
-	else {
+	}	else {
 		exit_code = 1;
 		fprintf (stderr, "invalid keyword: %s\n", tag);
 	}
@@ -1216,7 +1215,6 @@ _process_command (int argc, char *argv[])
 	return 0;
 }
 
-
 /*
  * _create_it - create a slurm configuration per the supplied arguments
  * IN argc - count of arguments
@@ -1424,6 +1422,8 @@ _show_it (int argc, char *argv[])
 		scontrol_print_step (val);
 	} else if (strncasecmp (tag, "topology", MAX(tag_len, 1)) == 0) {
 		scontrol_print_topo (val);
+	} else if (strncasecmp(tag, "licenses", MAX(tag_len, 2)) == 0) {
+		scontrol_print_licenses(val);
 	} else {
 		exit_code = 1;
 		if (quiet_flag != 1)
diff --git a/src/scontrol/scontrol.h b/src/scontrol/scontrol.h
index b7dc1d8fad4..d8eeb580fd6 100644
--- a/src/scontrol/scontrol.h
+++ b/src/scontrol/scontrol.h
@@ -160,5 +160,5 @@ extern int	scontrol_update_node (int argc, char *argv[]);
 extern int	scontrol_update_part (int argc, char *argv[]);
 extern int	scontrol_update_res (int argc, char *argv[]);
 extern int	scontrol_update_step (int argc, char *argv[]);
-
+extern void	scontrol_print_licenses(const char *feature);
 #endif
diff --git a/src/slurmctld/licenses.c b/src/slurmctld/licenses.c
index 4b7be506a04..3bbd53b799d 100644
--- a/src/slurmctld/licenses.c
+++ b/src/slurmctld/licenses.c
@@ -55,6 +55,7 @@
 
 List license_list = (List) NULL;
 static pthread_mutex_t license_mutex = PTHREAD_MUTEX_INITIALIZER;
+static void _pack_license(struct licenses *lic, Buf buffer, uint16_t protocol_version);
 
 /* Print all licenses on a list */
 static inline void _licenses_print(char *header, List licenses, int job_id)
@@ -68,12 +69,12 @@ static inline void _licenses_print(char *header, List licenses, int job_id)
 	iter = list_iterator_create(licenses);
   	while ((license_entry = (licenses_t *) list_next(iter))) {
 		if (job_id == 0) {
-			info("licenses: %s=%s total=%u used=%u", 
+			info("licenses: %s=%s total=%u used=%u",
 			     header, license_entry->name,
 			     license_entry->total, license_entry->used);
 		} else {
-			info("licenses: %s=%s job_id=%u available=%u used=%u", 
-			     header, license_entry->name, job_id, 
+			info("licenses: %s=%s job_id=%u available=%u used=%u",
+			     header, license_entry->name, job_id,
 			     license_entry->total, license_entry->used);
 		}
 	}
@@ -202,8 +203,8 @@ extern char *get_licenses_used(void)
 			if (licenses_used)
 				xstrcat(licenses_used, ",");
 			xstrfmtcat(licenses_used, "%s:%u/%u",
-				   license_entry->name, license_entry->used,
-				   license_entry->total);
+			           license_entry->name, license_entry->used,
+			           license_entry->total);
 		}
 		list_iterator_destroy(iter);
 	}
@@ -528,3 +529,83 @@ extern bool license_list_overlap(List list_1, List list_2)
 
 	return match;
 }
+
+/* pack_all_licenses()
+ *
+ * Return license counters to the library.
+ */
+extern void
+get_all_license_info(char **buffer_ptr,
+                     int *buffer_size,
+                     uid_t uid,
+                     uint16_t protocol_version)
+{
+	ListIterator iter;
+	licenses_t *lic_entry;
+	uint32_t lics_packed;
+	int tmp_offset;
+	Buf buffer;
+	time_t now = time(NULL);
+
+	debug2("%s: calling for all licenses", __func__);
+
+	buffer_ptr[0] = NULL;
+	*buffer_size = 0;
+
+	buffer = init_buf(BUF_SIZE);
+
+	/* write header: version and time
+	 */
+	lics_packed = 0;
+	pack32(lics_packed, buffer);
+	pack_time(now, buffer);
+
+	slurm_mutex_lock(&license_mutex);
+
+	if (license_list) {
+
+		iter = list_iterator_create(license_list);
+		while ((lic_entry = list_next(iter))) {
+			/* Now encode the license data structure.
+			 */
+			_pack_license(lic_entry, buffer, protocol_version);
+			++lics_packed;
+		}
+		list_iterator_destroy(iter);
+	}
+
+	slurm_mutex_unlock(&license_mutex);
+	debug2("%s: processed %d licenses", __func__, lics_packed);
+
+	/* put the real record count in the message body header
+	 */
+	tmp_offset = get_buf_offset(buffer);
+	set_buf_offset(buffer, 0);
+	pack32(lics_packed, buffer);
+	set_buf_offset(buffer, tmp_offset);
+
+	*buffer_size = get_buf_offset(buffer);
+	buffer_ptr[0] = xfer_buf_data(buffer);
+}
+
+/* pack_license()
+ *
+ * Encode the licenses data structure.
+ *
+ *	char *		name;
+ *	uint32_t	total;
+ *	uint32_t	used;
+ *
+ */
+static void
+_pack_license(struct licenses *lic, Buf buffer, uint16_t protocol_version)
+{
+	if (protocol_version >= SLURM_13_12_PROTOCOL_VERSION) {
+		packstr(lic->name, buffer);
+		pack32(lic->total, buffer);
+		pack32(lic->used, buffer);
+	} else {
+		error("\
+%s: protocol_version %hu not supported", __func__, protocol_version);
+	}
+}
diff --git a/src/slurmctld/licenses.h b/src/slurmctld/licenses.h
index 7f534a36ee3..e2b70bb13a5 100644
--- a/src/slurmctld/licenses.h
+++ b/src/slurmctld/licenses.h
@@ -118,4 +118,15 @@ extern List license_validate(char *licenses, bool *valid);
  */
 extern bool license_list_overlap(List list_1, List list_2);
 
+/* pack_all_licenses()
+ *
+ * Get the licenses and the usage counters in the io buffer
+ * to be sent out to the library
+ */
+extern void
+get_all_license_info(char **buffer_ptr,
+                     int *buffer_size,
+                     uid_t uid,
+                     uint16_t protocol_version);
+
 #endif /* !_LICENSES_H */
diff --git a/src/slurmctld/proc_req.c b/src/slurmctld/proc_req.c
index 17bc2067109..a41f0bf93c4 100644
--- a/src/slurmctld/proc_req.c
+++ b/src/slurmctld/proc_req.c
@@ -168,6 +168,7 @@ inline static void  _slurm_rpc_update_partition(slurm_msg_t * msg);
 inline static void  _slurm_rpc_update_block(slurm_msg_t * msg);
 inline static void  _slurm_rpc_dump_spank(slurm_msg_t * msg);
 inline static void  _slurm_rpc_dump_stats(slurm_msg_t * msg);
+inline static void  _slurm_rpc_dump_licenses(slurm_msg_t * msg);
 
 inline static void  _update_cred_key(void);
 
@@ -455,6 +456,10 @@ void slurmctld_req (slurm_msg_t * msg)
 		_slurm_rpc_dump_stats(msg);
 		slurm_free_stats_info_request_msg(msg->data);
 		break;
+	 case REQUEST_LICENSE_INFO:
+		 _slurm_rpc_dump_licenses(msg);
+		 slurm_free_license_info_request_msg(msg->data);
+		break;
 	default:
 		error("invalid RPC msg_type=%d", msg->msg_type);
 		slurm_send_rc_msg(msg, EINVAL);
@@ -4517,7 +4522,7 @@ inline static void _slurm_rpc_dump_spank(slurm_msg_t * msg)
 
 
 /* _slurm_rpc_dump_stats - process RPC for statistics information */
-static void _slurm_rpc_dump_stats(slurm_msg_t * msg)
+inline static void _slurm_rpc_dump_stats(slurm_msg_t * msg)
 {
 	char *dump;
 	int dump_size;
@@ -4559,3 +4564,44 @@ static void _slurm_rpc_dump_stats(slurm_msg_t * msg)
 	xfree(dump);
 }
 
+/* _slurm_rpc_dump_licenses()
+ *
+ * Pack the io buffer and send it back to the library.
+ */
+inline static void
+_slurm_rpc_dump_licenses(slurm_msg_t * msg)
+{
+	DEF_TIMERS;
+	char *dump;
+	int dump_size;
+	slurm_msg_t response_msg;
+	license_info_request_msg_t  *lic_req_msg;
+	uid_t uid = g_slurm_auth_get_uid(msg->auth_cred, NULL);
+
+	START_TIMER;
+	debug2("%s: Processing RPC: REQUEST_LICENSE_INFO uid=%d", __func__, uid);
+	lic_req_msg = (license_info_request_msg_t *)msg->data;
+
+	get_all_license_info(&dump, &dump_size, uid, msg->protocol_version);
+
+	END_TIMER2("_slurm_rpc_dump_licenses");
+	debug2("%s: size=%d %s", __func__, dump_size, TIME_STR);
+
+	/* init response_msg structure
+	 */
+	slurm_msg_t_init(&response_msg);
+
+	response_msg.flags = msg->flags;
+	response_msg.protocol_version = msg->protocol_version;
+	response_msg.address = msg->address;
+	response_msg.msg_type = RESPONSE_LICENSE_INFO;
+	response_msg.data = dump;
+	response_msg.data_size = dump_size;
+
+	/* send message
+	 */
+	slurm_send_node_msg(msg->conn_fd, &response_msg);
+	xfree(dump);
+	/* Ciao!
+	 */
+}
-- 
GitLab