सफल टूल कॉल के बाद भी SSH बैकग्राउंड जॉब चलते हैं
SSH बैकग्राउंड जॉब, nohup, जॉब कंट्रोल और अलग प्रक्रियाओं को जांचें, ताकि शून्य एग्जिट स्टेटस ऑडिट को जल्दी बंद न करे।

SSH टूल कॉल से मिला शून्य एग्जिट स्टेटस केवल यह साबित करता है कि रिमोट कमांड ने सफलता बताई। यह साबित नहीं करता कि उस कमांड से शुरू हुई हर प्रक्रिया रुक गई, उसने लिखना पूरा कर लिया या उसे सफल नतीजा मिला। अगर कमांड ने बैकग्राउंड में काम शुरू किया था, तो टूल के पूरा होने का रिकॉर्ड बनाते समय असल काम शायद शुरू ही हुआ हो।
मैं इस अंतर को shell की दिलचस्प आदत नहीं, ऑडिट की सीमा मानता हूं। कोई एजेंट डिप्लॉयमेंट स्क्रिप्ट चला सकता है, स्टेटस 0 पाकर आगे बढ़ सकता है, जबकि अलग हुई माइग्रेशन डेटा बदलती रहती है। टूल का रिकॉर्ड SSH चैनल के बारे में सही है, लेकिन रिमोट काम के बारे में गलत निष्कर्ष का संकेत देता है। समाधान है कि जिस जीवनचक्र को देखना है उसका स्पष्ट नाम रखें, उसे एजेंट वाले shell और टर्मिनल की उन्हीं स्थितियों में जांचें, और लंबे समय तक चलने वाली प्रक्रिया के मालिक सिस्टम से पूरा होने का प्रमाण लें।
शून्य रिमोट कमांड का है, उसकी संतान प्रक्रियाओं का नहीं
OpenSSH रिमोट कमांड का दिया एग्जिट स्टेटस लौटाता है, या SSH क्लाइंट में गलती होने पर 255 लौटाता है। RFC 4254 और साफ है: दूसरी ओर चल रही कमांड खत्म होने पर सर्वर exit-status चैनल अनुरोध भेज सकता है और उसके बाद चैनल बंद कर सकता है। कोई दस्तावेज यह नहीं कहता कि सर्वर कमांड की हर संतान प्रक्रिया का क्रमशः इंतजार करेगा।
जब भी shell कोई एसिंक्रोनस सूची चलाता है, यह फर्क मायने रखता है। POSIX के अनुसार & पर खत्म होने वाली कमांड एसिंक्रोनस है: shell उसे शुरू करता है और बिना इंतजार आगे बढ़ता है। अगर shell को और कुछ नहीं करना, तो वह सफलता से बंद हो सकता है, जबकि एसिंक्रोनस चाइल्ड अभी चल रहा हो। SSH स्टेटस उस shell का है।
लोग आम तौर पर कम से कम चार अलग नतीजों को सफलता कहते हैं:
- SSH कनेक्शन और प्रमाणीकरण सफल हुआ।
- रिमोट shell ने कमांड स्वीकार करके शुरू की।
- शुरू किया गया वर्कलोड स्टेटस 0 के साथ पूरा हुआ।
- अपेक्षित असर स्थायी हुआ और उसे देखा जा सकता है।
एक पूर्णांक चारों बातें साबित नहीं कर सकता। साफ ऑडिट रिकॉर्ड को बताना चाहिए कि उसे किस घटना ने बनाया। मैं चैनल के नतीजे के लिए ssh_command_exit_status और रिमोट काम के मालिक से मिले प्रमाण के लिए workload_result रखता हूं।
यही चेतावनी तब भी लागू होती है जब रिमोट executable खुद daemon बनता है। सफल fork के बाद लॉन्चर 0 लौटा सकता है, भले ही उसका चाइल्ड कुछ सेकंड बाद डेटाबेस खोलने, पोर्ट bind करने या कॉन्फिगरेशन पढ़ने में विफल हो जाए। लॉन्चर ने अपना अनुबंध निभाया, ऑडिटर ने गलत अनुबंध चुना।
बारह सेकंड की जांच अंतर दिखा देती है
इस भ्रामक सफलता को daemon, root पहुंच या असामान्य shell सेटिंग के बिना दोहराया जा सकता है। इसे किसी अस्थायी Unix खाते पर चलाएं। स्पष्ट redirection जरूरी हैं, क्योंकि वे बैकग्राउंड प्रक्रिया को SSH चैनल छोड़कर चलते रहने देते हैं।
ssh testhost 'rm -f /tmp/ssh-bg.done /tmp/ssh-bg.log; (sleep 12; date -u +%FT%TZ > /tmp/ssh-bg.done) > /tmp/ssh-bg.log 2>&1 < /dev/null & printf "launcher_pid=%s\n" "$!"'
printf 'ssh_status=%s\n' "$?"
ssh testhost 'test -f /tmp/ssh-bg.done; printf "done_status=%s\n" "$?"'
sleep 13
ssh testhost 'cat /tmp/ssh-bg.done'
तुरंत मिलने वाला सामान्य नतीजा ऐसा दिखता है:
launcher_pid=41872
ssh_status=0
done_status=1
2026-07-24T10:14:05Z
PID और समय अलग होंगे। यह विरोधाभास ही जांच का मकसद है: ssh_status=0 और done_status=1 साथ हो सकते हैं, क्योंकि वे अलग सवालों के जवाब हैं। shell ने एसिंक्रोनस सूची सफलतापूर्वक शुरू की, लेकिन marker file तब तक नहीं बनी थी।
इस उदाहरण को production orchestration न बनाएं। /tmp की marker files का नाम टकरा सकता है, वे मिट सकती हैं या पर्याप्त पहुंच वाली दूसरी प्रक्रिया उन्हें नकली बना सकती है। यह जांच समयक्रम दिखाने के लिए उपयोगी है। production completion record के लिए खास execution ID, सुरक्षित storage, प्रमाणित writer और साफ failure state चाहिए।
जांच को ठीक उसी रास्ते से दोहराएं जो एजेंट इस्तेमाल करता है। सीधे टर्मिनल की कमांड, noninteractive SSH exec अनुरोध, pseudo-terminal वाली SSH कॉल और tool gateway अलग startup files, shells और file descriptor व्यवस्था चुन सकते हैं। इन बातों को छोड़ने वाली जांच किसी पास के, पर अलग सिस्टम को जांचती है।
खुले file descriptor अलग काम को synchronous दिखा सकते हैं
बैकग्राउंड में चलाना और चैनल बंद होना अलग तंत्र हैं। SSH चैनल का standard output या standard error पाने वाली चाइल्ड प्रक्रिया, रिमोट shell के बंद होने के बाद भी चैनल को पढ़ने योग्य रख सकती है। स्थानीय ssh प्रक्रिया चाइल्ड का इंतजार करती हुई दिख सकती है, क्योंकि pipe अभी end of file तक नहीं पहुंची है, न कि इसलिए कि SSH चाइल्ड के नतीजे की निगरानी कर रहा है।
इन दोनों कॉल की तुलना करें और लगा समय मापें:
time ssh testhost 'sleep 12 &'
time ssh testhost 'sleep 12 > /tmp/sleep.log 2>&1 < /dev/null &'
OpenSSH और shell के आम मेल में पहली कॉल sleep खत्म होने तक खुली रह सकती है, जबकि दूसरी जल्दी लौटती है। इसे जांचने योग्य observation मानें, portable वादा नहीं। shell implementation, server behavior, pseudo-terminal allocation और चाइल्ड प्रोग्राम का descriptor handling नतीजा बदल सकते हैं।
यह अनचाहा इंतजार कमजोर प्रमाण है। बैकग्राउंड प्रक्रिया descriptor जल्दी बंद करके काम जारी रख सकती है। वह ऐसा grandchild बना सकती है जो descriptor बंद कर दे। वह socket से output भेज सकती है या सीधे storage में लिख सकती है। दूसरी ओर, केवल stdout खुला रखने वाला helper जरूरी काम विफल होने के बाद भी कॉल को व्यस्त दिखा सकता है।
फिर भी descriptor देखना उपयोगी है, क्योंकि इससे कई असंगत जांच समझ आती हैं। Linux पर रिमोट PID लें और SSH कॉल चलने के दौरान descriptor जांचें:
pid=$(cat /run/user/$(id -u)/agent-job.pid)
ps -o pid=,ppid=,pgid=,sid=,stat=,etime=,args= -p "$pid"
ls -l "/proc/$pid/fd/0" "/proc/$pid/fd/1" "/proc/$pid/fd/2"
parent PID, process group, session ID, state, elapsed time, command और descriptor 0, 1 और 2 के लक्ष्य दर्ज करें। अगर /proc नहीं है, तो operating system के अपने process और descriptor tools लें। जांच को pgrep name तक सीमित न करें: नाम टकराते हैं, wrapper नाम बदलते हैं और प्रक्रिया खत्म होने के बाद PID फिर इस्तेमाल हो सकता है।
nohup hangup संभालता है, जिम्मेदारी नहीं
nohup signal handling बदलता है, ताकि चलाया गया command SIGHUP को अनदेखा करे। वह command को बैकग्राउंड में नहीं भेजता। GNU Coreutils manual यह सीधे कहता है और एसिंक्रोनस execution के लिए & जोड़ने को कहता है। कॉपी किए deployment snippets में यह शर्त अक्सर खो जाती है।
SSH पर इसके redirection rules भी चौंकाते हैं। GNU nohup standard input को तभी redirect करता है जब वह terminal हो, standard output को nohup.out में तभी भेजता है जब stdout terminal हो, और आम तौर पर standard error के लिए भी वही फैसला लेता है। noninteractive SSH command अक्सर terminal के बजाय pipes इस्तेमाल करती है, इसलिए nohup इन descriptor को SSH चैनल से जुड़ा छोड़ सकता है।
इसलिए इन दोनों command के वादे अलग हैं:
ssh testhost 'nohup /opt/jobs/rebuild-index &'
ssh testhost 'nohup /opt/jobs/rebuild-index > /var/log/rebuild-index.log 2>&1 < /dev/null &'
दूसरा रूप standard descriptor को साफ तौर पर अलग करता है। फिर भी वह नहीं बताता कि rebuild-index पूरा हुआ या नहीं। nohup missing command जैसी invocation failure बताता है और बाकी मामलों में चलाए गए command का status लौटाता है। shell जब उस invocation को बैकग्राउंड में रख देता है, तो वह आम तौर पर job शुरू होने की सूचना देता है, उसके अंतिम status की नहीं।
SIGHUP से बचना जीवित रहने का केवल एक हिस्सा है। login manager session हटा सकता है, service manager session का control group खत्म कर सकता है, kernel out-of-memory policy लगा सकता है, administrator account रद्द कर सकता है या host reboot हो सकता है। प्रक्रिया पूरी तरह बचकर भी गलत नतीजा दे सकती है। nohup कोई identity, retry policy, resource boundary, durable status या भरोसेमंद completion record नहीं देता।
मैं छोटे, फेंके जा सकने वाले maintenance काम में अब भी nohup इस्तेमाल करता हूं, जब नतीजा खोना स्वीकार्य हो और मैं host देख रहा हूं। मैं इससे एजेंट की SSH कॉल को managed production job नहीं बनाता। यह सलाह इसलिए लोकप्रिय है कि snippet छोटा है और अक्सर terminal टूटने के बाद भी चलता है। जब किसी को बाद में साबित करना हो कि क्या पूरा हुआ, तब यह गलत है।
टर्मिनल जुड़ने पर job control बदलता है
Shell job control प्रक्रियाओं को group करता है, ताकि interactive user pipeline को रोक, फिर चला, foreground और background में भेज सके। noninteractive shell आम तौर पर monitor mode के बिना चलता है, और SSH exec request को pseudo-terminal तब तक नहीं मिलता जब तक client मांगे नहीं। jobs, %1, disown या terminal के signal पर निर्भर script एजेंट के हाथ में अलग व्यवहार कर सकती है।
POSIX job identifier और ज्ञात background PID को मौजूदा shell execution environment से जोड़ता है। उसका wait utility इन ज्ञात प्रक्रियाओं का इंतजार कर सकता है, पर दूसरे shell में शुरू हुआ wait job table नहीं पाता। यह ऑडिट तरीका विफल होता है:
ssh testhost 'long_task & printf "%s\n" "$!"'
ssh testhost 'wait 41872; printf "wait_status=%s\n" "$?"'
दूसरी कॉल नया shell शुरू करती है। 41872 अभी जीवित हो, तब भी यह shell उसे अपना child नहीं जानता। POSIX के अनुसार wait को दिया अज्ञात PID 127 लौटाता है। permissions और PID reuse इस संबंध को बाद में बनाने की कोशिश को और अविश्वसनीय बनाते हैं।
जब अनुबंध synchronous completion का हो, तो launch और wait एक ही shell में रखें:
ssh testhost 'long_task > /tmp/long-task.log 2>&1 < /dev/null & pid=$!; printf "pid=%s\n" "$pid"; wait "$pid"; rc=$?; printf "workload_status=%s\n" "$rc"; exit "$rc"'
यह pattern child status लौटाता है और SSH action खुली रखता है। यह उस child के लिए काम करता है जो उसी shell से जुड़ा रहे। अगर long_task fork करके अपनी मूल प्रक्रिया खत्म कर दे, तो wait असली worker से पहले पूरा हो सकता है। इस अनुबंध को मानने से पहले असली executable जांचें, उसकी जगह sleep नहीं।
Pseudo-terminal signal behavior और buffering भी बदलता है। session खत्म होने पर terminal SIGHUP भेज सकता है, और controlling terminal से पढ़ने वाले background process group को SIGTTIN मिलकर वे रुक सकते हैं। कुछ program terminal देखकर line buffering या अलग output अपनाते हैं। command को terminal semantics की सच में जरूरत न हो, तो automation को pseudo-terminal नहीं देना चाहिए और तीनों standard descriptor साफ तय करने चाहिए।
setsid प्रक्रिया अलग करता है, प्रमाण नहीं बनाता
setsid नई session और process group बनाता है, जिसके पास शुरू में controlling terminal नहीं होता। यह केवल SIGHUP अनदेखा करने से मजबूत terminal separation है। इसलिए child shell से लंबा जी सकता है और terminal के signal उसका पीछा छोड़ देते हैं।
इससे प्रक्रिया supervised नहीं होती। मूल parent के बंद होने पर दूसरा process descendant को अपना सकता है। सामान्य host पर वह PID 1 हो सकता है, जबकि container या service tree में subreaper हो सकता है। नया parent संबंध workload की सफलता नहीं बताता और launch action से जुड़ी सबसे आसान कड़ी मिटा सकता है।
उपयोगी separation test shell मिटने से पहले identity लेता है:
ssh testhost 'run_id=agent-probe-20260724-1014; setsid sh -c '\''printf "%s\n" "$$" > /tmp/'"$run_id"'.pid; sleep 12; printf "complete\n" > /tmp/'"$run_id"'.state'\'' > /tmp/'"$run_id"'.log 2>&1 < /dev/null & printf "run_id=%s\n" "$run_id"'
फिर मिले run ID से query करें, दर्ज PID को केवल संकेत मानें, और कोई action लेने से पहले process start time और command जांचें। अकेला PID durable identity नहीं है। प्रक्रिया खत्म हो और kernel उसका नंबर फिर दे दे, तो बाद की cleanup command किसी असंबंधित काम को निशाना बना सकती है।
Double fork, setsid, disown और descriptor बंद करना implementation techniques हैं। Teams अक्सर इन्हें job protocol समझती हैं, क्योंकि इनके बाद terminal लौट आता है। Job protocol दूसरे सवालों के जवाब देता है: अब काम का मालिक कौन है? उसे query कैसे करें? terminal states क्या हैं? exit reason कहां है? पूरा process tree कैसे cancel होगा? request, logs, effects और audit को कौन-सा ID जोड़ता है?
अगर launch response इन सवालों का जवाब नहीं दे सकता, तो उसे detached start लिखें, completed action नहीं।
ऑडिट अनुबंध में तीन lifecycle event तय करें
Audit किए जा सकने वाले remote action को acceptance, channel completion और workload completion के अलग event चाहिए। उन्हें एक success boolean में मिलाने से झूठा भरोसा पैदा होता है और incident reconstruction shell history पर निर्भर हो जाता है।
मैं इस तरह का वैचारिक record इस्तेमाल करता हूं:
{
"action_id": "act_01J3M8Q4",
"remote_host": "worker-07",
"launch": {"state": "accepted", "at": "2026-07-24T10:14:00Z"},
"ssh_command": {"state": "exited", "status": 0, "at": "2026-07-24T10:14:01Z"},
"workload": {"id": "job_8931", "state": "running", "result": null},
"completion_source": "remote-job-manager"
}
State के नाम separation से कम महत्वपूर्ण हैं। accepted का अर्थ है कि remote owner ने request जांचकर जिम्मेदारी ली। exited का अर्थ है कि SSH command खत्म हुई। running बताता है कि लंबा काम terminal state तक नहीं पहुंचा। workload का मालिक component ही उसके लिए succeeded, failed या cancelled लिखे।
Transition rules साफ रखें। Launch workload ID बनाने से पहले विफल हो सकता है। Remote system job स्वीकारने के बाद SSH channel टूट सकता है, जिससे caller failed नहीं बल्कि uncertain रह जाता है। साफ channel exit के बाद workload विफल हो सकता है। Cancellation request हुई हो, पर पूरी न हुई हो। unknown न दिखा सकने वाला audit model आखिर में अनुमान को तथ्य लिखेगा।
Idempotency भी इस अनुबंध का हिस्सा है। Submission के बाद client channel खो दे, तो उसे उसी action ID के साथ retry करके पूछना चाहिए कि remote owner ने पहले ही स्वीकार किया या नहीं। पहली response खोने पर दूसरी migration शुरू करना खराब log से ज्यादा गंभीर है।
Completion evidence में workload ID, terminal state, exit reason, start और finish timestamps और state देखने वाले manager की identity होनी चाहिए। जोखिम के अनुसार effect-specific proof जोड़ें, जैसे deployed revision, पूरा backup manifest या schema version। done वाली log line को अकेला प्रामाणिक स्रोत न मानें, जब तक logger और storage trusted job protocol का हिस्सा न हों।
केवल happy path नहीं, failure window जांचें
उपयोगी test matrix प्रक्रिया के अलग होने का तरीका, descriptor का व्यवहार और connection या process failure का समय बदलती है। इसे हर supported host class पर चलाएं, क्योंकि login manager, shell और service manager survival behavior बदलते हैं।
कम से कम ये मामले शामिल करें:
- Foreground command, shell background job,
nohupके साथ backgrounding,setsidकी नई session और खुद daemon बनने वाला program। - बिना terminal और allocated pseudo-terminal।
- Child को मिले descriptor, file में redirected descriptor और child द्वारा बंद descriptor।
- Acceptance से पहले disconnect, acceptance के बाद response से पहले, और SSH command exit के बाद।
- Child का nonzero exit, signal मिलना, रुक जाना, grandchild बनाना और explicit cancellation तक जीवित रहना।
हर मामले में चार समय लें: client start, launch acknowledgement, SSH channel close और remote terminal state। SSH status और workload result अलग रखें। job चलने पर process group और session जांचें, फिर साबित करें कि cancellation सभी descendant तक पहुंची।
छोटा harness status 0 के साथ terminal evidence न मिलने पर test fail कर सकता है:
result=$(ssh testhost '/usr/local/bin/job-submit agent-probe-42')
ssh_rc=$?
printf 'ssh_rc=%s response=%s\n' "$ssh_rc" "$result"
job_id=$(printf '%s\n' "$result" | sed -n 's/^job_id=//p')
test "$ssh_rc" -eq 0 && test -n "$job_id" || exit 1
/usr/local/bin/poll-job "$job_id" || exit 1
उदाहरण मानता है कि job-submit ठीक एक job_id= line लौटाता है और poll-job query प्रमाणित करता है, terminal state का इंतजार करता है और workload result के साथ निकलता है। ये अनुबंध की शर्तें हैं, SSH के गुण नहीं। असली harness में extra output अस्वीकार करें, deadline लगाएं, timeout पर uncertain state बचाएं और जांच के लिए raw response रखें।
Observer को भी जांचें। Launch के बाद agent रोकें। Client machine restart करें। SSH credential बदलें। अगर job को reboot के बाद चलना है, तो remote host reboot करें। अगर job ID की अकेली copy किसी agent के context window में है, तो system audit योग्य नहीं है।
लंबे काम का मालिक आम तौर पर service manager होना चाहिए
जब काम को SSH command से लंबा जीना हो, तो उसे remote service या job manager को दें और उसका durable ID लौटाएं। Manager को process group का मालिक होना, output लेना, resources और cancellation लागू करना, status बचाना और running को terminal state से अलग बताने वाली query देनी चाहिए।
systemd host पर transient या templated service control group और journal identity देती है। systemd-run manual asynchronous service startup को service termination के इंतजार से अलग बताता है और चेतावनी देता है कि simple service fork के बाद, target program के execute होने से पहले startup सफल मान सकती है। Execution failure दिखना जरूरी हो, तो मैं Type=exec पसंद करता हूं, लेकिन वह भी केवल startup साबित करता है, अंतिम job success नहीं।
Templated unit ownership boundary इस तरह बना सकती है:
[Unit]
Description=Agent job %i
[Service]
Type=exec
ExecStart=/usr/local/libexec/agent-job %i
StandardOutput=journal
StandardError=journal
KillMode=control-group
TimeoutStopSec=30s
एक unique, validated instance ID submit करें और unit को terminal state तक query करें। ActiveState, SubState, Result, ExecMainStatus, timestamps और instance ID दर्ज करें। workload के fork करने पर व्यवहार जांचें, क्योंकि service type और program को सहमत होना चाहिए। सख्त validation के बिना मनमाने user input को unit name या command argument न बनाएं।
Queue, batch scheduler, container orchestrator या application-specific job table यही ownership boundary दे सकते हैं। उस owner को चुनें जो workload के resources और recovery का पहले से जिम्मेदार है। SSH को submit और query करना चाहिए, shell operators की जटिल chain से scheduler बनने का दिखावा नहीं।
छोटे काम के लिए command को foreground में रखना और उसका असली status लौटाना आसान और अक्सर बेहतर है। Detachment की कीमत है: दूसरा state store, दूसरी identity, cancellation semantics, retention और reconciliation। यह कीमत तभी दें जब काम को सच में call से ज्यादा चलना हो।
ऑडिट की कहानी remote terminal state पर खत्म होती है
Action gateway SSH call को सही दर्ज कर सकता है, बिना यह जाने कि remote descendant मौजूद है। Sallyport SSH action को Activity journal में और agent run को Sessions journal में लिखता है, इसलिए call entry channel result का प्रमाण है, host की process सूची नहीं। Remote job ID और उसका terminal event फिर भी साफ, audit योग्य action से वापस आना चाहिए।
यह बंटवारा हर record को ईमानदार रखता है। Gateway साबित करता है कि किस agent run ने SSH बुलाया, किस सुरक्षित key ने action मंजूर किया, कौन-सी call हुई और क्या result लौटा। Remote manager submission के बाद की घटना साबित करता है। दोनों को ऐसे action ID से जोड़ें जिसे agent launch और query के बीच चुपचाप बदल न सके।
Detached launch को UI या log में completed न लिखें। submitted या detached लिखें, workload ID दिखाएं और parent action खुली या साफ pending रखें, जब तक trusted observer terminal state दर्ज न करे। Observation timeout हो, तो unknown दिखाकर reconciliation मांगें। लाल status असुविधाजनक हो सकता है, पर गलत process पर आधारित हरा status खतरनाक है।
Approval timing को भी यही सटीकता चाहिए। SSH key use की human approval उस समय दिखाई जानकारी के तहत एक कोशिश की मंजूरी देती है। Click असीम descendant के हर future action को मंजूरी नहीं देता और अंतिम effect प्रमाणित नहीं करता। Submitted job घंटों चल सकती या आगे process बना सकती है, तो launch से पहले यह बताएं और approval को action ID, host, command intent और remote job type से बांधें। Per-call approval record और workload completion record एक chain में हैं, लेकिन अलग फैसले बताते हैं।
हर boundary पर raw output बचाएं। Parse करने से पहले submission response रखें, protocol अनुमति दे तो stderr अलग लें, और लिखें कि pseudo-terminal ने stream मिला दिए या नहीं। Parser duplicate job ID, control characters, अधूरा response और एक response को दूसरे जैसा दिखाने वाली extra line अस्वीकार करे। Parsed fields automation के लिए हैं; raw bytes बाद में parser, shell quoting या remote program की गलती जांचने के लिए। दोनों से secrets बाहर रखें।
Completion marker के लिए atomic publication rule भी चाहिए। Worker पहले protected storage की temporary file में result लिखे, durability जरूरी हो तो data flush करे और पूरी record तैयार होने पर ही file rename करे। Query side भरोसा करने से पहले action ID, expected owner, file type और state जांचे। इससे बेहतर है कि service manager या database authenticated interface से status दे। /tmp में सबके लिखने योग्य marker timing दिखाती है, incident तय नहीं करती।
Submission और cancellation दोनों में uncertain delivery की योजना बनाएं। Remote manager के job स्वीकारने के बाद और client को ID मिलने से पहले SSH connection गायब हो, तो सही local state unknown है। मूल idempotency key से दोबारा जुड़कर manager से वही submission खोजें। चुपचाप फिर submit न करें। Cancellation का जवाब खो जाए, तो manager के terminal state और खाली process group बताने तक query करें। भेजा signal कोशिश है, काम रुकने का प्रमाण नहीं।
Reconciliation को agent process से ज्यादा चलना चाहिए। Unresolved action ID को अस्थायी conversation से बाहर रखें, owner तय करें और periodic query से पुराने records बंद या escalate करें। Deadline को failure से अलग रखें: job caller deadline पार करके भी स्वस्थ और owned हो सकती है। Audit record बताए कि caller ने कब इंतजार छोड़ा, observation का owner कौन है और बाद में completion आया या नहीं। वरना timeout एक और झूठा terminal state बनता है।
Reviewers को runtime वाली ही शब्दावली चाहिए। ऐसे actions खोजें जिनमें ssh_command.status शून्य है और workload.state गायब, deadline से आगे running या unknown है। ऐसे terminal jobs खोजें जिनकी launch approval नहीं और ऐसी repeated submissions जो idempotency key साझा करती हैं। ये query lifecycle के फर्क को gap खोजने वाला control बनाती हैं।
Operators को remote record फिर खोलने के लिए एक action दें। View में last observation time, उसे देने वाला component और status live query या cached data से आया, यह दिखना चाहिए। Queue साफ करने के लिए unknown को failed न बदलें। Remote owner के जवाब या authorized reviewer के documented evidence से समाधान तक uncertainty बचाएं। Outage के बाद कुछ खुले records असहज होंगे, और वही सही है।
Creation के साथ retention भी जांचें। Workload records सबसे लंबे expected job और audit या incident review अवधि से ज्यादा समय तक query किए जा सकें। Service manager transient unit detail तुरंत हटाता हो, तो collection से पहले terminal result को durable action record में copy करें। अगले सप्ताह कोई system जिस job ID को resolve न कर सके, वह संबंध बनाती है, जवाबदेही नहीं।
एजेंट के असली रास्ते में बारह सेकंड वाली जांच चलाएं, फिर महत्वपूर्ण workload के साथ दोहराएं। Remote completion marker बनने से पहले SSH card हरा हो जाए, तो audit gap मिल गया। शून्य को रखें, क्योंकि वह command के बारे में सही प्रमाण है। उससे उस काम की गवाही मांगना बंद करें जिसे उसने कभी देखा ही नहीं।
सामान्य प्रश्न
क्या SSH बैकग्राउंड प्रक्रिया खत्म होने तक इंतजार करता है?
SSH remote command और channel data बंद होने का इंतजार करता है, हर descendant tree का नहीं। खुला descriptor रखने वाली background process client को रोक सकती है, पर यह संयोग है, supervision नहीं।
Remote job चलते समय ssh 0 क्यों लौटाता है?
Remote shell asynchronous command सफलतापूर्वक शुरू करके status 0 के साथ बंद हो सकता है। SSH वही status बताता है, जबकि background job का अपना result बाद में आता है।
क्या nohup SSH command को background में चलाता है?
नहीं। nohup SIGHUP handling बदलता है; shell operator & background execution करता है। Standard input, output और error कहां जाएं, यह भी तय करना होगा।
nohup के साथ SSH कभी अटका हुआ क्यों लगता है?
Noninteractive session में stdout और stderr terminal के बजाय pipes हो सकते हैं, इसलिए nohup उन्हें वैसा छोड़ सकता है। उन्हें खुला रखने वाला descendant channel closure को देर करता है।
क्या दूसरी SSH call में remote PID का wait कर सकता हूं?
नया shell ऐसी process पर wait नहीं कर सकता जो उसका child नहीं और उसकी job table में नहीं है। Shell parentage बनाने के बजाय durable job ID से manager query करें।
क्या भरोसेमंद detached job के लिए setsid काफी है?
setsid नई session बनाकर controlling terminal का संबंध हटाता है। वह durable identity, state storage, retry, पूरा cancellation या success proof नहीं जोड़ता।
क्या automated SSH command को pseudo-terminal देना चाहिए?
आम तौर पर नहीं, जब तक command को terminal behavior की जरूरत न हो। Pseudo-terminal signal, buffering, job control और input बदलकर manual test और agent run का फर्क छिपा सकता है।
Background SSH job के audit log में क्या होना चाहिए?
Launch acceptance, SSH channel exit और workload completion अलग events में लिखें। Remote ID, terminal state, exit reason, timestamps, completion source और records जोड़ने वाला action ID रखें।
Detached remote process को सुरक्षित तरीके से कैसे cancel करें?
Job के owner manager से cancel कराएं और cancelled या failed terminal state जांचें। याद रखा PID मारना असुरक्षित है, क्योंकि descendant बच सकते हैं और PID दोबारा इस्तेमाल हो सकता है।
Foreground SSH command कब job manager से बेहतर है?
जब काम सीमित हो, connection खुला रह सके और command असली workload status लौटाए, तब foreground command लें। Disconnect के बाद चलने, बाद में query या पूरे group cancellation के लिए job manager लें।