@@ -108,6 +108,12 @@ function main {
108108 return
109109 fi
110110
111+ # Don't allow launching sinteractive from inside an existing session
112+ if [[ -n " $TMUX " ]] && [[ " $TMUX " == * sinteractive* ]]; then
113+ echo 1>&2 " Error: Already inside an sinteractive session. Exit this session first."
114+ exit 1
115+ fi
116+
111117 # Parse custom flags before passing remaining args to sbatch
112118 local args=()
113119 local reattach_jobid=' '
@@ -193,9 +199,17 @@ function main {
193199 set -- --signal=B:USR1@600 " $@ "
194200 fi
195201
202+ # Check interactive job limit before submitting
203+ local partition
204+ partition=$( get_partition_from_args " $@ " )
205+
206+ if ! check_job_limit " $partition " ; then
207+ exit 1
208+ fi
209+
196210 local output
197211 local sbatch_ec
198- output=$( sbatch " $@ " " ${BASH_SOURCE[0]} " tmux)
212+ output=$( sbatch --output=/dev/null --error=/dev/null " $@ " " ${BASH_SOURCE[0]} " tmux)
199213 sbatch_ec=$?
200214
201215 JOB_ID=$( <<< " ${output}" egrep -o -e ' \b[0-9]+$' )
@@ -255,8 +269,43 @@ function main {
255269 echo 1>&2 ' '
256270 echo 1>&2 " To cancel the job:"
257271 echo 1>&2 " scancel ${JOB_ID} "
272+
273+ # Show other running sinteractive sessions
274+ local other_running
275+ other_running=$( squeue --me --states RUNNING --noheader \
276+ --Format=JobID:12,Comment:30,NodeList:16,TimeUsed:12,TimeLimit:12 2> /dev/null \
277+ | grep sinteractive | grep -v " ^${JOB_ID} " )
278+
279+ if [[ -n " $other_running " ]]; then
280+ echo 1>&2 ' '
281+ echo 1>&2 ' Other running sinteractive sessions:'
282+ printf 1>&2 ' %-10s %-14s %-10s %-10s\n' ' JOBID' ' NODE' ' ELAPSED' ' TIMELIMIT'
283+ while read -r jobid comment node elapsed limit; do
284+ printf 1>&2 ' %-10s %-14s %-10s %-10s\n' " $jobid " " $node " " $elapsed " " $limit "
285+ done <<< " $other_running"
286+ fi
258287 else
259288 echo 1>&2 " Session ended. Job ${JOB_ID} is no longer running."
289+
290+ # Show any remaining sinteractive sessions
291+ local remaining
292+ remaining=$( squeue --me --states RUNNING --noheader \
293+ --Format=JobID:12,Comment:30,NodeList:16,TimeUsed:12,TimeLimit:12 2> /dev/null \
294+ | grep sinteractive)
295+
296+ if [[ -n " $remaining " ]]; then
297+ echo 1>&2 ' '
298+ echo 1>&2 ' You have other sinteractive sessions still running:'
299+ printf 1>&2 ' %-10s %-14s %-10s %-10s\n' ' JOBID' ' NODE' ' ELAPSED' ' TIMELIMIT'
300+ while read -r jobid comment node elapsed limit; do
301+ printf 1>&2 ' %-10s %-14s %-10s %-10s\n' " $jobid " " $node " " $elapsed " " $limit "
302+ done <<< " $remaining"
303+ echo 1>&2 ' '
304+ echo 1>&2 ' To free slots, cancel sessions you no longer need:'
305+ while read -r jobid _rest; do
306+ echo 1>&2 " scancel $jobid "
307+ done <<< " $remaining"
308+ fi
260309 fi
261310}
262311
@@ -281,6 +330,100 @@ function list_sessions {
281330 exit 0
282331}
283332
333+ # Extract the effective partition name from the processed argument list.
334+ function get_partition_from_args {
335+ while [[ $# -gt 0 ]]; do
336+ case " $1 " in
337+ --partition=* )
338+ echo " ${1# --partition=} "
339+ return
340+ ;;
341+ -p)
342+ echo " $2 "
343+ return
344+ ;;
345+ * )
346+ shift
347+ ;;
348+ esac
349+ done
350+ echo " interactive"
351+ }
352+
353+ # Check if submitting another job would exceed the QOS MaxJobsPerUser limit.
354+ # Only checks when targeting the interactive partition.
355+ # Falls through silently if sacctmgr or squeue are unavailable.
356+ function check_job_limit {
357+ local partition=" $1 "
358+
359+ # Only enforce for the interactive partition
360+ if [[ " $partition " != " interactive" ]]; then
361+ return 0
362+ fi
363+
364+ # Query QOS limit dynamically; fail open if sacctmgr unavailable
365+ local max_jobs
366+ max_jobs=$( sacctmgr show qos interactive format=MaxJobsPerUser --noheader --parsable2 2> /dev/null)
367+ if [[ $? -ne 0 ]] || [[ -z " $max_jobs " ]]; then
368+ return 0
369+ fi
370+
371+ max_jobs=$( echo " $max_jobs " | xargs)
372+
373+ if ! [[ " $max_jobs " =~ ^[0-9]+$ ]]; then
374+ return 0
375+ fi
376+
377+ # Count current running + pending jobs on the interactive partition
378+ local current_jobs
379+ current_jobs=$( squeue --me --partition=interactive --states=RUNNING,PENDING --noheader 2> /dev/null | wc -l)
380+ if [[ $? -ne 0 ]]; then
381+ return 0
382+ fi
383+
384+ current_jobs=$( echo " $current_jobs " | xargs)
385+
386+ if (( current_jobs >= max_jobs )) ; then
387+ echo 1>&2 " "
388+ echo 1>&2 " Error: You already have ${current_jobs} /${max_jobs} interactive jobs (the maximum allowed)."
389+ echo 1>&2 " "
390+
391+ local lines
392+ lines=$( squeue --me --partition=interactive --states=RUNNING,PENDING --noheader \
393+ --Format=JobID:12,Name:20,NodeList:16,State:10,TimeUsed:12,TimeLimit:12 2> /dev/null)
394+
395+ if [[ -n " $lines " ]]; then
396+ echo 1>&2 " Your current interactive jobs:"
397+ echo 1>&2 " "
398+ printf 1>&2 ' %-10s %-18s %-14s %-9s %-10s %-10s\n' \
399+ ' JOBID' ' NAME' ' NODE' ' STATE' ' ELAPSED' ' TIMELIMIT'
400+ while IFS= read -r line; do
401+ read -r jobid name node state elapsed limit <<< " $line"
402+ printf 1>&2 ' %-10s %-18s %-14s %-9s %-10s %-10s\n' \
403+ " $jobid " " $name " " $node " " $state " " $elapsed " " $limit "
404+ done <<< " $lines"
405+ echo 1>&2 " "
406+ fi
407+
408+ echo 1>&2 " To reattach to an existing session:"
409+ local jobids
410+ jobids=$( squeue --me --partition=interactive --states=RUNNING --noheader --Format=JobID 2> /dev/null | xargs)
411+ for jid in $jobids ; do
412+ echo 1>&2 " sinteractive --attach $jid "
413+ done
414+ echo 1>&2 " "
415+ echo 1>&2 " To free a slot, cancel a session:"
416+ jobids=$( squeue --me --partition=interactive --states=RUNNING,PENDING --noheader --Format=JobID 2> /dev/null | xargs)
417+ for jid in $jobids ; do
418+ echo 1>&2 " scancel $jid "
419+ done
420+ echo 1>&2 " "
421+ return 1
422+ fi
423+
424+ return 0
425+ }
426+
284427function reattach {
285428 local jobid=$1
286429 local sock=" sinteractive-${jobid} "
0 commit comments