1 /*
2  * alloc.c - convert data between resource allocation related messages and perl HVs
3  */
4 
5 #include <EXTERN.h>
6 #include <perl.h>
7 #include <XSUB.h>
8 #include <slurm/slurm.h>
9 #define NEED_sv_2pv_flags_GLOBAL
10 #include "ppport.h"
11 
12 #include "slurm-perl.h"
13 
14 static void _free_environment(char** environ);
15 
16 /*
17  * convert perl HV to job_desc_msg_t
18  * return 0 on success, -1 on failure
19  */
20 int
hv_to_job_desc_msg(HV * hv,job_desc_msg_t * job_desc)21 hv_to_job_desc_msg(HV *hv, job_desc_msg_t *job_desc)
22 {
23 	SV **svp;
24 	HV *environ_hv;
25 	AV *argv_av;
26 	SV *val;
27 	char *env_key, *env_val;
28 	I32 klen;
29 	STRLEN vlen;
30 	int num_keys, i;
31 
32 	slurm_init_job_desc_msg(job_desc);
33 
34 	FETCH_FIELD(hv, job_desc, account, charp, FALSE);
35 	FETCH_FIELD(hv, job_desc, acctg_freq, charp, FALSE);
36 	FETCH_FIELD(hv, job_desc, alloc_node, charp, FALSE);
37 	FETCH_FIELD(hv, job_desc, alloc_resp_port, uint16_t, FALSE);
38 	FETCH_FIELD(hv, job_desc, alloc_sid, uint32_t, FALSE);
39 	/* argv, argc */
40 	if((svp = hv_fetch(hv, "argv", 4, FALSE))) {
41 		if(SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVAV) {
42 			argv_av = (AV*)SvRV(*svp);
43 			job_desc->argc = av_len(argv_av) + 1;
44 			if (job_desc->argc > 0) {
45 				Newz(0, job_desc->argv, (int32_t)(job_desc->argc + 1), char*);
46 				for(i = 0; i < job_desc->argc; i ++) {
47 					if((svp = av_fetch(argv_av, i, FALSE)))
48 						*(job_desc->argv + i) = (char*) SvPV_nolen(*svp);
49 					else {
50 						Perl_warn(aTHX_ "error fetching `argv' of job descriptor");
51 						free_job_desc_msg_memory(job_desc);
52 						return -1;
53 					}
54 				}
55 			}
56 		} else {
57 			Perl_warn(aTHX_ "`argv' of job descriptor is not an array reference, ignored");
58 		}
59 	}
60 	FETCH_FIELD(hv, job_desc, array_inx, charp, FALSE);
61 	FETCH_FIELD(hv, job_desc, begin_time, time_t, FALSE);
62 	FETCH_FIELD(hv, job_desc, comment, charp, FALSE);
63 	FETCH_FIELD(hv, job_desc, contiguous, uint16_t, FALSE);
64 	FETCH_FIELD(hv, job_desc, cpu_bind, charp, FALSE);
65 	FETCH_FIELD(hv, job_desc, cpu_bind_type, uint16_t, FALSE);
66 	FETCH_FIELD(hv, job_desc, dependency, charp, FALSE);
67 	FETCH_FIELD(hv, job_desc, end_time, time_t, FALSE);
68 
69 	/* environment, env_size */
70 	if ((svp = hv_fetch(hv, "environment", 11, FALSE))) {
71 		if (SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV) {
72 			environ_hv = (HV*)SvRV(*svp);
73 			num_keys = HvKEYS(environ_hv);
74 			job_desc->env_size = num_keys;
75 			Newz(0, job_desc->environment, num_keys + 1, char*);
76 
77 			hv_iterinit(environ_hv);
78 			i = 0;
79 			while ((val = hv_iternextsv(environ_hv, &env_key, &klen))) {
80 				env_val = SvPV(val, vlen);
81 				Newz(0, (*(job_desc->environment + i)), klen + vlen + 2, char);
82 				sprintf(*(job_desc->environment + i), "%s=%s", env_key, env_val);
83 				i ++;
84 			}
85 		} else {
86 			Perl_warn(aTHX_ "`environment' of job descriptor is not a hash reference, ignored");
87 		}
88 	}
89 
90 	FETCH_FIELD(hv, job_desc, exc_nodes, charp, FALSE);
91 	FETCH_FIELD(hv, job_desc, features, charp, FALSE);
92 	FETCH_FIELD(hv, job_desc, tres_per_job, charp, FALSE);
93 	FETCH_FIELD(hv, job_desc, tres_per_node, charp, FALSE);
94 	FETCH_FIELD(hv, job_desc, tres_per_socket, charp, FALSE);
95 	FETCH_FIELD(hv, job_desc, tres_per_task, charp, FALSE);
96 	FETCH_FIELD(hv, job_desc, group_id, uint32_t, FALSE);
97 	FETCH_FIELD(hv, job_desc, immediate, uint16_t, FALSE);
98 	FETCH_FIELD(hv, job_desc, job_id, uint32_t, FALSE);
99 	FETCH_FIELD(hv, job_desc, kill_on_node_fail, uint16_t, FALSE);
100 	FETCH_FIELD(hv, job_desc, licenses, charp, FALSE);
101 	FETCH_FIELD(hv, job_desc, mail_type, uint16_t, FALSE);
102 	FETCH_FIELD(hv, job_desc, mail_user, charp, FALSE);
103 	FETCH_FIELD(hv, job_desc, mem_bind, charp, FALSE);
104 	FETCH_FIELD(hv, job_desc, mem_bind_type, uint16_t, FALSE);
105 	FETCH_FIELD(hv, job_desc, name, charp, FALSE);
106 	FETCH_FIELD(hv, job_desc, network, charp, FALSE);
107 	FETCH_FIELD(hv, job_desc, nice, uint16_t, FALSE);
108 	FETCH_FIELD(hv, job_desc, num_tasks, uint32_t, FALSE);
109 	FETCH_FIELD(hv, job_desc, open_mode, uint8_t, FALSE);
110 	FETCH_FIELD(hv, job_desc, other_port, uint16_t, FALSE);
111 	FETCH_FIELD(hv, job_desc, overcommit, uint16_t, FALSE);
112 	FETCH_FIELD(hv, job_desc, partition, charp, FALSE);
113 	FETCH_FIELD(hv, job_desc, plane_size, uint16_t, FALSE);
114 	FETCH_FIELD(hv, job_desc, priority, uint32_t, FALSE);
115 	FETCH_FIELD(hv, job_desc, profile, uint32_t, FALSE);
116 	FETCH_FIELD(hv, job_desc, qos, charp, FALSE);
117 	FETCH_FIELD(hv, job_desc, resp_host, charp, FALSE);
118 	FETCH_FIELD(hv, job_desc, req_nodes, charp, FALSE);
119 	FETCH_FIELD(hv, job_desc, requeue, uint16_t, FALSE);
120 	FETCH_FIELD(hv, job_desc, reservation, charp, FALSE);
121 	FETCH_FIELD(hv, job_desc, script, charp, FALSE);
122 	FETCH_FIELD(hv, job_desc, shared, uint16_t, FALSE);
123 	/* spank_job_env, spank_job_env_size */
124 	if((svp = hv_fetch(hv, "spank_job_env", 13, FALSE))) {
125 		if(SvROK(*svp) && SvTYPE(SvRV(*svp)) == SVt_PVHV) {
126 			environ_hv = (HV*)SvRV(*svp);
127 			num_keys = HvKEYS(environ_hv);
128 			job_desc->spank_job_env_size = num_keys;
129 			Newz(0, job_desc->spank_job_env, num_keys + 1, char*);
130 
131 			hv_iterinit(environ_hv);
132 			i = 0;
133 			while((val = hv_iternextsv(environ_hv, &env_key, &klen))) {
134 				env_val = SvPV(val, vlen);
135 				Newz(0, (*(job_desc->spank_job_env + i)), klen + vlen + 2, char);
136 				sprintf(*(job_desc->spank_job_env + i), "%s=%s", env_key, env_val);
137 				i ++;
138 			}
139 		}
140 		else {
141 			Perl_warn(aTHX_ "`spank_job_env' of job descriptor is not a hash reference, ignored");
142 		}
143 	}
144 
145 	FETCH_FIELD(hv, job_desc, task_dist, uint16_t, FALSE);
146 	FETCH_FIELD(hv, job_desc, time_limit, uint32_t, FALSE);
147 	FETCH_FIELD(hv, job_desc, time_min, uint32_t, FALSE);
148 	FETCH_FIELD(hv, job_desc, user_id, uint32_t, FALSE);
149 	FETCH_FIELD(hv, job_desc, wait_all_nodes, uint16_t, FALSE);
150 	FETCH_FIELD(hv, job_desc, warn_signal, uint16_t, FALSE);
151 	FETCH_FIELD(hv, job_desc, warn_time, uint16_t, FALSE);
152 	FETCH_FIELD(hv, job_desc, work_dir, charp, FALSE);
153 	/* job constraints: */
154 	FETCH_FIELD(hv, job_desc, cpu_freq_min, uint32_t, FALSE);
155 	FETCH_FIELD(hv, job_desc, cpu_freq_max, uint32_t, FALSE);
156 	FETCH_FIELD(hv, job_desc, cpu_freq_gov, uint32_t, FALSE);
157 	FETCH_FIELD(hv, job_desc, cpus_per_task, uint16_t, FALSE);
158 	FETCH_FIELD(hv, job_desc, min_cpus, uint32_t, FALSE);
159 	FETCH_FIELD(hv, job_desc, max_cpus, uint32_t, FALSE);
160 	FETCH_FIELD(hv, job_desc, min_nodes, uint32_t, FALSE);
161 	FETCH_FIELD(hv, job_desc, max_nodes, uint32_t, FALSE);
162 	FETCH_FIELD(hv, job_desc, sockets_per_node, uint16_t, FALSE);
163 	FETCH_FIELD(hv, job_desc, cores_per_socket, uint16_t, FALSE);
164 	FETCH_FIELD(hv, job_desc, threads_per_core, uint16_t, FALSE);
165 	FETCH_FIELD(hv, job_desc, ntasks_per_node, uint16_t, FALSE);
166 	FETCH_FIELD(hv, job_desc, ntasks_per_socket, uint16_t, FALSE);
167 	FETCH_FIELD(hv, job_desc, ntasks_per_core, uint16_t, FALSE);
168 	FETCH_FIELD(hv, job_desc, pn_min_cpus, uint16_t, FALSE);
169 	FETCH_FIELD(hv, job_desc, pn_min_memory, uint64_t, FALSE);
170 	FETCH_FIELD(hv, job_desc, pn_min_tmp_disk, uint32_t, FALSE);
171 	FETCH_FIELD(hv, job_desc, reboot, uint16_t, FALSE);
172 	FETCH_PTR_FIELD(hv, job_desc, select_jobinfo, "Slurm::dynamic_plugin_data_t",  FALSE);
173 
174 	FETCH_FIELD(hv, job_desc, std_err, charp, FALSE);
175 	FETCH_FIELD(hv, job_desc, std_in, charp, FALSE);
176 	FETCH_FIELD(hv, job_desc, std_out, charp, FALSE);
177 	FETCH_FIELD(hv, job_desc, wckey, charp, FALSE);
178 	return 0;
179 }
180 
181 /*
182  * free allocated environment variable memory for job_desc_msg_t
183  */
184 static void
_free_environment(char ** environ)185 _free_environment(char** environ)
186 {
187 	int i;
188 	if(! environ)
189 		return;
190 	for(i = 0; *(environ + i) ; i ++)
191 		Safefree(*(environ + i));
192 	Safefree(environ);
193 }
194 
195 /*
196  * free allocate memory for job_desc_msg_t
197  */
198 void
free_job_desc_msg_memory(job_desc_msg_t * msg)199 free_job_desc_msg_memory(job_desc_msg_t *msg)
200 {
201 	if (msg->argv)
202 		Safefree (msg->argv);
203 	_free_environment(msg->environment);
204 	_free_environment(msg->spank_job_env);
205 }
206 
207 /*
208  * convert resource_allocation_resource_msg_t to perl HV
209  */
210 int
resource_allocation_response_msg_to_hv(resource_allocation_response_msg_t * resp_msg,HV * hv)211 resource_allocation_response_msg_to_hv(resource_allocation_response_msg_t *resp_msg, HV *hv)
212 {
213 	AV *av;
214 	int i;
215 
216 	STORE_FIELD(hv, resp_msg, job_id, uint32_t);
217 	if(resp_msg->node_list)
218 		STORE_FIELD(hv, resp_msg, node_list, charp);
219 	STORE_FIELD(hv, resp_msg, num_cpu_groups, uint16_t);
220 	if(resp_msg->num_cpu_groups) {
221 		av = newAV();
222 		for(i = 0; i < resp_msg->num_cpu_groups; i ++) {
223 			av_store_uint16_t(av, i, resp_msg->cpus_per_node[i]);
224 		}
225 		hv_store_sv(hv, "cpus_per_node", newRV_noinc((SV*)av));
226 
227 		av = newAV();
228 		for(i = 0; i < resp_msg->num_cpu_groups; i ++) {
229 			av_store_uint32_t(av, i, resp_msg->cpu_count_reps[i]);
230 		}
231 		hv_store_sv(hv, "cpu_count_reps", newRV_noinc((SV*)av));
232 	}
233 	STORE_FIELD(hv, resp_msg, node_cnt, uint32_t);
234 	STORE_FIELD(hv, resp_msg, error_code, uint32_t);
235 	STORE_PTR_FIELD(hv, resp_msg, select_jobinfo, "Slurm::dynamic_plugin_data_t");
236 
237 	return 0;
238 }
239 
240 /*
241  * convert submit_response_msg_t to perl HV
242  */
243 int
submit_response_msg_to_hv(submit_response_msg_t * resp_msg,HV * hv)244 submit_response_msg_to_hv(submit_response_msg_t *resp_msg, HV* hv)
245 {
246 	STORE_FIELD(hv, resp_msg, job_id, uint32_t);
247 	STORE_FIELD(hv, resp_msg, step_id, uint32_t);
248 	STORE_FIELD(hv, resp_msg, error_code, uint32_t);
249 	return 0;
250 }
251 
252 /*
253  * convert job_sbcast_cred_msg_t to perl HV
254  */
255 int
job_sbcast_cred_msg_to_hv(job_sbcast_cred_msg_t * msg,HV * hv)256 job_sbcast_cred_msg_to_hv(job_sbcast_cred_msg_t *msg, HV *hv)
257 {
258 	AV *av;
259 	int i;
260 
261 	STORE_FIELD(hv, msg, job_id, uint32_t);
262 	STORE_FIELD(hv, msg, node_cnt, uint32_t);
263 	if(msg->node_cnt) {
264 		av = newAV();
265 		for(i = 0; i < msg->node_cnt; i ++) {
266 			/* XXX: This is a packed inet address */
267 			av_store(av, i, newSVpvn((char*)(msg->node_addr + i), sizeof(slurm_addr_t)));
268 		}
269 		hv_store_sv(hv, "node_addr", newRV_noinc((SV*)av));
270 	}
271 	if (msg->node_list)
272 		STORE_FIELD(hv, msg, node_list, charp);
273 	STORE_PTR_FIELD(hv, msg, sbcast_cred, "Slurm::sbcast_cred_t");
274 	return 0;
275 }
276 
277 int
srun_job_complete_msg_to_hv(srun_job_complete_msg_t * msg,HV * hv)278 srun_job_complete_msg_to_hv(srun_job_complete_msg_t *msg, HV *hv)
279 {
280 	STORE_FIELD(hv, msg, job_id, uint32_t);
281 	STORE_FIELD(hv, msg, step_id, uint32_t);
282 	return 0;
283 }
284 
285 int
srun_timeout_msg_to_hv(srun_timeout_msg_t * msg,HV * hv)286 srun_timeout_msg_to_hv(srun_timeout_msg_t *msg, HV *hv)
287 {
288 	STORE_FIELD(hv, msg, job_id, uint32_t);
289 	STORE_FIELD(hv, msg, step_id, uint32_t);
290 	STORE_FIELD(hv, msg, timeout, time_t);
291 	return 0;
292 }
293 
294 
295 
296 /********** pending_callback for slurm_allocate_resources_blocking() **********/
297 static SV* sarb_cb_sv = NULL;
298 
299 void
set_sarb_cb(SV * callback)300 set_sarb_cb(SV *callback)
301 {
302 	if (callback == NULL) {
303 		if (sarb_cb_sv != NULL)
304 			sv_setsv(sarb_cb_sv, &PL_sv_undef);
305 	} else {
306 		if (sarb_cb_sv == NULL)
307 			sarb_cb_sv = newSVsv(callback);
308 		else
309 			sv_setsv(sarb_cb_sv, callback);
310 	}
311 }
312 
313 void
sarb_cb(uint32_t job_id)314 sarb_cb(uint32_t job_id)
315 {
316 	dSP;
317 
318 	if (sarb_cb_sv == NULL ||
319 	    sarb_cb_sv == &PL_sv_undef)
320 	    	return;
321 
322 	ENTER;
323 	SAVETMPS;
324 	PUSHMARK(SP);
325 	XPUSHs(sv_2mortal(newSVuv(job_id)));
326 	PUTBACK;
327 
328 	call_sv(sarb_cb_sv, G_VOID | G_DISCARD);
329 
330 	FREETMPS;
331 	LEAVE;
332 }
333 
334 /********** convert functions for callbacks **********/
335 
336 static int
srun_ping_msg_to_hv(srun_ping_msg_t * msg,HV * hv)337 srun_ping_msg_to_hv(srun_ping_msg_t *msg, HV *hv)
338 {
339 	STORE_FIELD(hv, msg, job_id, uint32_t);
340 	STORE_FIELD(hv, msg, step_id, uint32_t);
341 	return 0;
342 }
343 
344 static int
srun_user_msg_to_hv(srun_user_msg_t * msg,HV * hv)345 srun_user_msg_to_hv(srun_user_msg_t *msg, HV *hv)
346 {
347 	STORE_FIELD(hv, msg, job_id, uint32_t);
348 	STORE_FIELD(hv, msg, msg, charp);
349 	return 0;
350 }
351 
352 static int
srun_node_fail_msg_to_hv(srun_node_fail_msg_t * msg,HV * hv)353 srun_node_fail_msg_to_hv(srun_node_fail_msg_t *msg, HV *hv)
354 {
355 	STORE_FIELD(hv, msg, job_id, uint32_t);
356 	STORE_FIELD(hv, msg, nodelist, charp);
357 	STORE_FIELD(hv, msg, step_id, uint32_t);
358 	return 0;
359 }
360 
361 /*********** callbacks for slurm_allocation_msg_thr_create() **********/
362 static SV *ping_cb_sv = NULL;
363 static SV *job_complete_cb_sv = NULL;
364 static SV *timeout_cb_sv = NULL;
365 static SV *user_msg_cb_sv = NULL;
366 static SV *node_fail_cb_sv = NULL;
367 
368 void
set_sacb(HV * callbacks)369 set_sacb(HV *callbacks)
370 {
371 	SV **svp, *cb;
372 
373 	if (callbacks == NULL) {
374 		if (ping_cb_sv != NULL)
375 			sv_setsv(ping_cb_sv, &PL_sv_undef);
376 		if (job_complete_cb_sv != NULL)
377 			sv_setsv(job_complete_cb_sv, &PL_sv_undef);
378 		if (timeout_cb_sv != NULL)
379 			sv_setsv(timeout_cb_sv, &PL_sv_undef);
380 		if (user_msg_cb_sv != NULL)
381 			sv_setsv(user_msg_cb_sv, &PL_sv_undef);
382 		if (node_fail_cb_sv != NULL)
383 			sv_setsv(node_fail_cb_sv, &PL_sv_undef);
384 		return;
385 	}
386 
387 	svp = hv_fetch(callbacks, "ping", 4, FALSE);
388 	cb = svp ? *svp : &PL_sv_undef;
389 	if (ping_cb_sv == NULL) {
390 		ping_cb_sv = newSVsv(cb);
391 	} else {
392 		sv_setsv(ping_cb_sv, cb);
393 	}
394 
395 	svp = hv_fetch(callbacks, "job_complete", 4, FALSE);
396 	cb = svp ? *svp : &PL_sv_undef;
397 	if (job_complete_cb_sv == NULL) {
398 		job_complete_cb_sv = newSVsv(cb);
399 	} else {
400 		sv_setsv(job_complete_cb_sv, cb);
401 	}
402 
403 	svp = hv_fetch(callbacks, "timeout", 4, FALSE);
404 	cb = svp ? *svp : &PL_sv_undef;
405 	if (timeout_cb_sv == NULL) {
406 		timeout_cb_sv = newSVsv(cb);
407 	} else {
408 		sv_setsv(timeout_cb_sv, cb);
409 	}
410 
411 	svp = hv_fetch(callbacks, "user_msg", 4, FALSE);
412 	cb = svp ? *svp : &PL_sv_undef;
413 	if (user_msg_cb_sv == NULL) {
414 		user_msg_cb_sv = newSVsv(cb);
415 	} else {
416 		sv_setsv(user_msg_cb_sv, cb);
417 	}
418 
419 	svp = hv_fetch(callbacks, "node_fail", 4, FALSE);
420 	cb = svp ? *svp : &PL_sv_undef;
421 	if (node_fail_cb_sv == NULL) {
422 		node_fail_cb_sv = newSVsv(cb);
423 	} else {
424 		sv_setsv(node_fail_cb_sv, cb);
425 	}
426 }
427 
428 static void
ping_cb(srun_ping_msg_t * msg)429 ping_cb(srun_ping_msg_t *msg)
430 {
431 	HV *hv;
432 	dSP;
433 
434 	if (ping_cb_sv == NULL ||
435 	    ping_cb_sv == &PL_sv_undef) {
436 		return;
437 	}
438 	hv = newHV();
439 	if (srun_ping_msg_to_hv(msg, hv) < 0) {
440 		Perl_warn( aTHX_ "failed to convert surn_ping_msg_t to perl HV");
441 		SvREFCNT_dec(hv);
442 		return;
443 	}
444 
445 	ENTER;
446 	SAVETMPS;
447 	PUSHMARK(SP);
448 	XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
449 	PUTBACK;
450 
451 	call_sv(ping_cb_sv, G_VOID);
452 
453 	FREETMPS;
454 	LEAVE;
455 }
456 
457 static void
job_complete_cb(srun_job_complete_msg_t * msg)458 job_complete_cb(srun_job_complete_msg_t *msg)
459 {
460 	HV *hv;
461 	dSP;
462 
463 	if (job_complete_cb_sv == NULL ||
464 	    job_complete_cb_sv == &PL_sv_undef) {
465 		return;
466 	}
467 	hv = newHV();
468 	if (srun_job_complete_msg_to_hv(msg, hv) < 0) {
469 		Perl_warn( aTHX_ "failed to convert surn_job_complete_msg_t to perl HV");
470 		SvREFCNT_dec(hv);
471 		return;
472 	}
473 
474 	ENTER;
475 	SAVETMPS;
476 	PUSHMARK(SP);
477 	XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
478 	PUTBACK;
479 
480 	call_sv(job_complete_cb_sv, G_VOID);
481 
482 	FREETMPS;
483 	LEAVE;
484 }
485 
486 static void
timeout_cb(srun_timeout_msg_t * msg)487 timeout_cb(srun_timeout_msg_t *msg)
488 {
489 	HV *hv;
490 	dSP;
491 
492 	if (timeout_cb_sv == NULL ||
493 	    timeout_cb_sv == &PL_sv_undef) {
494 		return;
495 	}
496 	hv = newHV();
497 	if (srun_timeout_msg_to_hv(msg, hv) < 0) {
498 		Perl_warn( aTHX_ "failed to convert surn_timeout_msg_t to perl HV");
499 		SvREFCNT_dec(hv);
500 		return;
501 	}
502 
503 	ENTER;
504 	SAVETMPS;
505 	PUSHMARK(SP);
506 	XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
507 	PUTBACK;
508 
509 	call_sv(timeout_cb_sv, G_VOID);
510 
511 	FREETMPS;
512 	LEAVE;
513 }
514 
515 static void
user_msg_cb(srun_user_msg_t * msg)516 user_msg_cb(srun_user_msg_t *msg)
517 {
518 	HV *hv;
519 	dSP;
520 
521 	if (user_msg_cb_sv == NULL ||
522 	    user_msg_cb_sv == &PL_sv_undef) {
523 		return;
524 	}
525 	hv = newHV();
526 	if (srun_user_msg_to_hv(msg, hv) < 0) {
527 		Perl_warn( aTHX_ "failed to convert surn_user_msg_msg_t to perl HV");
528 		SvREFCNT_dec(hv);
529 		return;
530 	}
531 
532 	ENTER;
533 	SAVETMPS;
534 	PUSHMARK(SP);
535 	XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
536 	PUTBACK;
537 
538 	call_sv(user_msg_cb_sv, G_VOID);
539 
540 	FREETMPS;
541 	LEAVE;
542 }
543 
544 static void
node_fail_cb(srun_node_fail_msg_t * msg)545 node_fail_cb(srun_node_fail_msg_t *msg)
546 {
547 	HV *hv;
548 	dSP;
549 
550 	if (node_fail_cb_sv == NULL ||
551 	    node_fail_cb_sv == &PL_sv_undef) {
552 		return;
553 	}
554 	hv = newHV();
555 	if (srun_node_fail_msg_to_hv(msg, hv) < 0) {
556 		Perl_warn( aTHX_ "failed to convert surn_node_fail_msg_t to perl HV");
557 		SvREFCNT_dec(hv);
558 		return;
559 	}
560 
561 	ENTER;
562 	SAVETMPS;
563 	PUSHMARK(SP);
564 	XPUSHs(sv_2mortal(newRV_noinc((SV*)hv)));
565 	PUTBACK;
566 
567 	call_sv(node_fail_cb_sv, G_VOID);
568 
569 	FREETMPS;
570 	LEAVE;
571 }
572 
573 slurm_allocation_callbacks_t sacb = {
574 	ping_cb,
575 	job_complete_cb,
576 	timeout_cb,
577 	user_msg_cb,
578 	node_fail_cb
579 };
580