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