A limpeza de processos remotos funciona depois do cancelamento SSH?
A limpeza de processos remotos após o cancelamento SSH exige grupos de processos, status persistente, testes com processos filhos e um plano honesto para desconexões.

Uma execução de agente cancelada não é o mesmo evento que um comando remoto interrompido. O processo local pode sair corretamente enquanto o transporte SSH continua ativo, o transporte pode desaparecer enquanto o shell remoto continua executando, e o shell pode morrer enquanto seus filhos continuam em outro grupo de processos. Se você transformar todos esses eventos em um único status chamado «cancelado», mais cedo ou mais tarde deixará uma migração de banco de dados, uma instalação de pacote, um worker de testes ou um auxiliar de implantação rodando depois que o agente informar que parou.
A solução não é apenas criar um truque inteligente com tratamento de sinais. Você precisa de um contrato de cancelamento que identifique a execução remota, crie um limite encerrável ao redor dos descendentes, preserve saída suficiente para diagnosticar uma execução interrompida e tenha uma resposta no lado remoto para o caso de o controlador desaparecer. Crie e teste esse contrato antes de permitir que um agente execute comandos com efeitos colaterais.
O cancelamento SSH tem quatro etapas separadas
Uma solicitação de cancelamento precisa atravessar quatro limites: o agente decide parar, o supervisor local interrompe ou sinaliza o cliente SSH, o protocolo SSH transporta um evento de canal ou sinal, e o host remoto reage a esse evento. Cada etapa pode falhar de forma independente.
A RFC 4254 separa esses conceitos. Ela define uma mensagem de fechamento de canal e, separadamente, uma solicitação de canal signal para nomes como TERM, INT e HUP. O fechamento de um canal é um evento de transporte. Ele não significa «enviar SIGTERM a todos os descendentes remotos». A RFC também diz que os dados enviados antes do fechamento devem ser entregues, se possível. «Se possível» deixa muita coisa em aberto quando um laptop entra em suspensão, uma rota de rede cai ou um processo local é encerrado à força.
Essa distinção revela um erro comum de projeto:
- Um agente inicia
ssh host long-command. - A pessoa usuária pressiona cancelar.
- O executor do agente encerra o processo filho local.
- A interface marca o trabalho como cancelado.
long-command, ou um de seus filhos, continua no host.
A quinta linha não é uma exceção. Ela é o resultado padrão quando o servidor não tem motivo para encerrar o comando ou quando o comando se separou da sessão antes de a conexão desaparecer.
Um contrato de cancelamento útil diz exatamente o que o lado local tentará fazer e o que pertence ao lado remoto:
- O iniciador cria uma execução remota identificável com um ID aleatório.
- O wrapper remoto inicia o trabalho em seu próprio grupo de processos ou sessão.
- Um cancelamento normal envia
TERMpara esse grupo de processos e registra o resultado. - O wrapper só escala para
KILLdepois de um período de tolerância definido. - Um prazo remoto ou uma concessão encerra o trabalho quando o controlador não retorna.
- A saída e o status final sobrevivem ao fluxo SSH.
Não chame uma tarefa de cancelada até obter um de dois resultados: um registro remoto final confirmado ou um resultado explícito de «estado desconhecido». Fingir certeza depois de uma perda de conexão torna a resposta a incidentes mais lenta, porque todos começam com uma premissa falsa.
Um PID remoto não basta para limpar os filhos
Encerrar o PID do shell remoto só é seguro quando o shell nunca cria processos, inicia um pipeline, executa um processo em segundo plano ou chama uma ferramenta que cria auxiliares. Poucos comandos têm esse perfil.
Considere este comando remoto comum:
build-assets | tee build.log &
wait
O shell tem um PID. O pipeline tem vários processos. tee pode continuar gravando o log depois que o shell sair. Um compilador pode iniciar processos workers. Um gerenciador de pacotes pode delegar o trabalho a um serviço. Se você executar kill -TERM "$shell_pid", terá encerrado um membro de um grupo maior e aprendido muito pouco sobre os demais.
Os grupos de processos oferecem a unidade correta de cancelamento para uma execução remota curta. No Linux, cada processo pertence a um grupo de processos, e cada grupo pertence a uma sessão. Os sinais gerados pelo terminal vão para o grupo de processos em primeiro plano, por isso o comportamento do terminal pode parecer mais misterioso do que realmente é. A documentação do Linux para setpgid(2) também deixa claro que um filho herda o grupo de processos do pai, a menos que algo altere isso.
Para um comando controlado por agente, crie uma sessão nova para o trabalho. Normalmente, o líder da sessão tem um PID igual ao PGID e ao SID. Assim, um PID negativo em kill se dirige ao grupo de processos:
kill -TERM -- -"$pgid"
O sinal de menos inicial é o que diferencia o encerramento de um processo do encerramento de seu grupo de processos. O -- também é importante. Ele impede que um valor malformado seja interpretado como uma opção.
Não presuma que o PID do trabalho seja seu ID de grupo de processos. Verifique isso no início. Um wrapper de shell, um gerenciador de serviços ou um programa que chama setpgid pode alterar a árvore. Este é o menor comando de inspeção que vale a pena manter no seu conjunto de testes:
ps -o pid=,ppid=,pgid=,sid=,stat=,etime=,command= -p "$pid"
Uma saída típica tem este formato:
24182 24177 24182 24182 Ss 00:03 bash ./worker.sh /tmp/agent-runs/6c4...
Aqui, PID, PGID e SID são iguais. Isso é uma evidência de que kill -TERM -- -24182 alcança o limite pretendido. Se o PGID não corresponder ao registro da execução, interrompa o início em vez de tentar adivinhar.
Um grupo de processos ainda tem limites. Um filho pode chamar setsid, um runtime de contêiner pode mover um processo para outro lugar, e um trabalho pode pedir a um gerenciador de serviços que execute algo fora do grupo. Esse comportamento às vezes é legítimo. Ele também significa que sua garantia de cancelamento terminou nessa transferência. Trate o trabalho separado como um tipo distinto, com identidade, operação de parada e registro de auditoria próprios.
Um wrapper de cancelamento precisa de um caminho de limpeza real
Um wrapper de shell remoto deve ser responsável pelo PID do trabalho, capturar os sinais de encerramento esperados, alcançar o grupo do trabalho, aguardar brevemente e gravar um registro final. Ele não deve usar pkill command-name, examinar uma lista de processos sem vínculo ou encerrar todos os processos pertencentes a uma conta. Esses atalhos funcionam até o dia em que duas execuções de agente compartilham um usuário, o nome do host muda ou um nome de comando coincide com o trabalho de outra pessoa.
Este fixture orientado ao Linux é propositalmente simples. Ele cria um diretório de execução protegido, inicia um trabalho em uma sessão nova, grava a saída em arquivos e encerra o grupo do trabalho quando o wrapper recebe TERM, INT ou HUP.
#!/usr/bin/env bash
set -Eeuo pipefail
run_id=${1:?run ID required}
shift
run_dir="${HOME}/.agent-runs/${run_id}"
umask 077
mkdir -p "$run_dir"
child_pid=""
child_pgid=""
finished=0
write_status() {
local state=$1
local code=${2:-}
local tmp="$run_dir/status.tmp"
printf '{"run_id":"%s","state":"%s","exit_code":"%s"}\n' \
"$run_id" "$state" "$code" >"$tmp"
mv "$tmp" "$run_dir/status.json"
}
stop_group() {
if [[ -z ${child_pgid:-} ]]; then
return
fi
kill -TERM -- "-$child_pgid" 2>/dev/null || true
for _ in 1 2 3 4 5; do
if ! kill -0 -- "-$child_pgid" 2>/dev/null; then
return
fi
sleep 1
done
kill -KILL -- "-$child_pgid" 2>/dev/null || true
}
cancel() {
local signal=$1
trap - TERM INT HUP
write_status "cancelling:$signal"
stop_group
wait "$child_pid" 2>/dev/null || true
write_status "cancelled:$signal"
finished=1
exit 143
}
trap 'cancel TERM' TERM
trap 'cancel INT' INT
trap 'cancel HUP' HUP
write_status "starting"
setsid "$@" >"$run_dir/stdout.log" 2>"$run_dir/stderr.log" &
child_pid=$!
child_pgid=$(ps -o pgid= -p "$child_pid" | tr -d ' ')
if [[ "$child_pgid" != "$child_pid" ]]; then
printf 'unexpected PGID for %s: %s\n' "$child_pid" "$child_pgid" \
>"$run_dir/stderr.log"
kill -TERM "$child_pid" 2>/dev/null || true
write_status "launch_failed"
exit 70
fi
printf '%s\n' "$child_pid" >"$run_dir/pid"
printf '%s\n' "$child_pgid" >"$run_dir/pgid"
write_status "running"
set +e
wait "$child_pid"
code=$?
set -e
if [[ $finished -eq 0 ]]; then
write_status "finished" "$code"
fi
exit "$code"
Esse wrapper evita uma falha específica: um sinal de cancelamento chega ao wrapper, mas ele encerra apenas a si próprio e deixa o trabalho perdido. Ele não promete interromper descendentes que se separaram intencionalmente. Não deve fazer essa promessa.
O manual do Linux para setsid(2) diz que setsid() cria uma nova sessão e transforma o chamador no líder de um novo grupo de processos, inicialmente sem terminal de controle. O comando setsid do util-linux executa um programa nessa nova sessão, criando um processo filho quando necessário. Por isso, ele é um limite prático para uma execução de comando, não uma chave mágica de limpeza.
Mantenha o wrapper restrito. Ele deve iniciar, registrar, interromper e informar. Não esconda nele a lógica de negócio. O trabalho ainda deve ter seu próprio tratamento de transações, limpeza de arquivos temporários e regras de idempotência.
Uma desconexão limpa do transporte não garante a limpeza
Usuários de SSH costumam tirar conclusões demais de um teste no terminal. Executam um comando com PTY, fecham o terminal, veem um processo sair depois de SIGHUP e concluem que a limpeza por desconexão funciona. Depois, um agente usa um canal SSH não interativo sem PTY e o comportamento muda.
Um PTY cria semântica de terminal. Uma desconexão do terminal pode levar à entrega de SIGHUP, mas apenas nas condições que governam terminais de controle e grupos de processos em primeiro plano. O manual do Linux descreve SIGHUP como uma desconexão de um terminal de controle ou a morte de um processo de controle. Essa descrição não diz que toda desconexão SSH sinaliza todos os processos iniciados pelo SSH.
O SSH não interativo costuma ser o melhor padrão para agentes, pois oferece uma saída mais limpa e menos surpresas causadas pela inicialização do shell. Ele também elimina qualquer dependência acidental do comportamento do terminal. Use um PTY somente quando o programa remoto precisar dele, como um instalador antigo que se recusa a executar de outra forma. Nesse caso, documente que o PTY faz parte do comportamento do comando e teste-o separadamente.
Há três casos de desconexão que vale a pena nomear:
O cliente envia um cancelamento deliberado
O supervisor local ainda tem uma conexão ativa e pode enviar um sinal de protocolo ou abrir um comando de controle autenticado separado que sinaliza o PGID registrado. Esse é o melhor caso. O wrapper remoto recebe TERM, faz a limpeza e grava cancelled:TERM.
Não dependa do recebimento de SIGINT local por um cliente SSH para que exatamente isso aconteça, a menos que você tenha testado a biblioteca e a chamada do cliente que distribui. Um cliente de terminal, uma biblioteca SSH integrada e uma ferramenta MCP podem mapear o cancelamento local de maneiras diferentes. Alguns fecham um socket. Alguns encerram o processo local. Alguns conseguem enviar uma solicitação SSH signal. São implementações diferentes de uma interface que os usuários chamam de «cancelar».
O cliente local falha ou perde a rede
O comando remoto pode continuar. O servidor não consegue distinguir um problema temporário de roteamento de um usuário que pretende deixar o trabalho continuar, a menos que seu protocolo diga como fazer isso. Uma concessão remota é a resposta honesta.
No início, grave um deadline_epoch no diretório remoto da execução. Um supervisor local o renova enquanto a execução continuar autorizada. Um watchdog remoto verifica esse valor e chama o mesmo caminho de limpeza do grupo de processos depois que ele expira. Escolha uma duração de concessão adequada à operação. Cinco minutos podem servir para um comando de shell. Podem ser imprudentes para uma compilação com fases longas, mas normalmente silenciosas.
O host remoto falha ou reinicia
Você pode perder tanto o processo quanto o status final. Não informe «cancelado» ou «concluído» apenas porque a conexão SSH terminou. Marque a execução como desconhecida até que uma reconciliação posterior leia o diário do host, o estado da implantação, o registro de bloqueio ou um resultado específico da aplicação.
A parte difícil não é emitir uma palavra de status. É se recusar a emitir um status que seu sistema não consegue sustentar.
Uma saída parcial prova observação, não conclusão
Um fluxo responde «quais bytes o cliente recebeu até agora?». Ele não responde «em que estado o comando remoto deixou o sistema?». Esse erro aparece quando um programa remoto imprime done antes de descarregar seu último arquivo ou quando a rede cai depois que o comando terminou, mas antes de o cliente receber o status de saída do SSH.
Mantenha dois registros:
stdout.logestderr.logguardam a saída de diagnóstico enquanto o trabalho a produz.status.jsoné um pequeno registro final gravado atomicamente depois que o wrapper observa a saída ou trata o cancelamento.
O mv do wrapper é importante. Grave um arquivo de status temporário no mesmo diretório e depois renomeie-o para o local definitivo. Os leitores verão o arquivo anterior completo ou o novo arquivo completo. Nunca devem analisar metade de um documento JSON e inventar um resultado.
A própria saída precisa de regras. Um comando pode armazenar muita saída em buffer quando stdout é um arquivo, em vez de um terminal. Se o progresso for importante, faça o trabalho emitir um status explícito, orientado por linhas, em stderr ou use um arquivo de progresso no nível da aplicação. Não resolva o buffering alocando um PTY para todos os comandos. Isso altera o comportamento e pode misturar stdout com stderr, o que piora a auditoria e a análise de falhas.
Trate estes casos de forma diferente na interface ou no diário do agente:
| Registro remoto | Estado do fluxo | Significado |
|---|---|---|
finished, código de saída presente | completo | O wrapper observou a conclusão normal. |
cancelled:TERM | pode terminar abruptamente | O wrapper iniciou o cancelamento e interrompeu o grupo. |
apenas cancelling:TERM | desconectado | A limpeza começou, mas o registro final não foi observado. Faça a reconciliação. |
apenas running, concessão válida | desconectado | O trabalho ainda pode estar em execução. Não tente novamente às cegas. |
| nenhum registro utilizável | desconectado | O estado é desconhecido. Verifique os efeitos colaterais antes de iniciar novamente. |
Evite colocar tokens de acesso, dumps de configuração sem mascaramento ou credenciais nesses logs. A injeção de credenciais SSH pode manter a chave privada longe do agente, mas um comando remoto ainda pode imprimir segredos que leu do próprio ambiente ou da configuração. A retenção da saída faz parte do desenho do comando, não é um detalhe posterior.
Teste árvores de processos, não apenas um shell inerte
trap 'exit' TERM; sleep 600 é um teste fraco de cancelamento. Ele prova que um shell em primeiro plano pode receber um sinal. Não testa filhos, grupos de processos, limpeza atrasada, persistência da saída ou um shell remoto que desaparece no momento errado.
Use um trabalho que crie uma árvore de processos visível e registre cada sinal. Salve isto como worker.sh em um host Linux descartável:
#!/usr/bin/env bash
set -Eeuo pipefail
run_dir=${1:?run directory required}
note() {
printf '%s pid=%s pgid=%s %s\n' \
"$(date +%s)" "$$" "$(ps -o pgid= -p $$ | tr -d ' ')" "$1" \
>>"$run_dir/worker.log"
}
trap 'note TERM; exit 143' TERM
trap 'note INT; exit 130' INT
trap 'note HUP; exit 129' HUP
(
trap 'note grandchild_TERM; exit 143' TERM
trap 'note grandchild_HUP; exit 129' HUP
while :; do
note grandchild_tick
sleep 1
done
) &
grandchild=$!
note "started grandchild=$grandchild"
while :; do
note parent_tick
sleep 1
done
Execute-o pelo wrapper com um ID de execução aleatório. Em outra sessão SSH, inspecione a árvore de processos e o diretório da execução:
run_id=cancel-test-$(date +%s)
ssh host.example './remote-wrapper.sh '"$run_id"' ./worker.sh \"$HOME/.agent-runs/'"$run_id"'\"'
As aspas exatas serão diferentes em um iniciador real. Tudo bem. O que não pode mudar são as evidências do teste: você precisa do ID da execução remota, do PID do wrapper, do PGID do trabalho, dos locais dos logs e do status final.
Depois, teste estes caminhos de falha, um de cada vez:
- Envie
TERMao PID do wrapper. Confirme que o processo pai e o neto registraram o encerramento e quepsnão encontra nenhum processo no PGID registrado. - Envie
TERMdiretamente ao PID do trabalho. Confirme que não se presume um comportamento seguro dos filhos. Esse teste explica por que o wrapper alcança um grupo. - Encerre o cliente SSH local sem enviar um sinal remoto. Confirme que o trabalho permanece ativo até a expiração da concessão remota. Se ele parar imediatamente, registre o motivo, como o comportamento de desligamento do PTY, em vez de tratar esse resultado como universal.
- Desconecte depois que o wrapper gravar
cancelling:TERM, mas antes de gravar seu registro final. Confirme que a reconciliação consegue distinguir uma observação incompleta de um novo trabalho em execução. - Inicie duas execuções na mesma conta, cancele uma e prove que a outra continua viva. Isso detecta lógica perigosa de
pkillamplo e de limpeza de toda a conta.
Use ps e pgrep -a -g "$pgid" durante os testes e repita depois do período de tolerância. Verifique o arquivo de status remoto e os logs somente depois de verificar a tabela de processos. Um registro de status que diz «cancelado» enquanto os workers continuam vivos é um bug no supervisor, não uma simples divergência de relatório.
Sinais de morte do pai ajudam apenas dentro de um worker controlado
O Linux oferece PR_SET_PDEATHSIG, que permite a um processo pedir ao kernel que envie um sinal quando o thread que o criou terminar. Isso pode ser útil quando você controla um pequeno auxiliar nativo que cria um filho imediato e quer que esse filho pare se o auxiliar morrer. A configuração sobrevive a execve em casos comuns, mas o manual documenta exceções importantes, incluindo mudanças de credenciais.
Isso não resolve sozinho a limpeza de agentes remotos.
Primeiro, o processo do servidor SSH não é necessariamente o pai com o qual você se importa. Segundo, o sinal se aplica ao processo que o configurou, não automaticamente a todos os descendentes. Terceiro, um comando remoto que cria processos, usa criação dupla de processos ou entrega o trabalho a outro serviço abandona essa relação. Quarto, essa função é específica do Linux, o que importa se sua frota tiver hosts Unix variados.
Use-a apenas como reforço quando a árvore de processos estiver sob seu controle. Por exemplo, um worker Linux pequeno pode configurar PR_SET_PDEATHSIG antes de executar um único filho controlado, enquanto o wrapper externo continua responsável pelo grupo de processos e pela concessão. Isso oferece dois detectores de falha com escopos diferentes. Não permite ignorar o limite do grupo nem o registro remoto final.
O mesmo alerta vale para nohup, disown e setsid dentro do trabalho. Eles são úteis quando alguém quer deliberadamente que o trabalho sobreviva a um terminal. São incompatíveis com a promessa de que cancelar uma execução do agente interrompe o trabalho. Torne essa escolha explícita no início.
Um segundo canal de controle costuma ser mais limpo do que encerrar o primeiro
Quando um agente cancela uma chamada SSH em andamento, seu próprio contexto de execução pode já estar sendo desmontado. Depender desse processo que está morrendo para enviar um último sinal de protocolo cria condições de corrida. Um processo supervisor separado deve ser responsável pelo cancelamento e pela reconciliação.
Um projeto possível é este:
- O supervisor gera um ID de execução criptograficamente aleatório e chama o wrapper remoto.
- O wrapper registra seu PID, PGID, horário de início e status em um diretório com o nome desse ID.
- O supervisor registra o ID da execução e o host remoto antes de começar a consumir a saída.
- Ao cancelar, o supervisor abre uma nova ação de controle que lê o registro da execução, verifica a propriedade e a idade esperadas e sinaliza o PGID registrado.
- O supervisor consulta o registro de status final até encontrar um estado terminal ou atingir um prazo de relatório.
A ação de controle precisa validar o registro da execução antes de sinalizar qualquer coisa. No mínimo, verifique se o diretório do registro pertence ao usuário esperado, se o PID ainda existe, se o PGID registrado corresponde ao ps e se o horário de início é compatível com o processo iniciado. O Linux pode reutilizar PIDs. Um arquivo PID antigo acompanhado de um kill incondicional é como um script de limpeza com falha termina um trabalho que não tem relação com ele semanas depois.
Não coloque o comando de limpeza atrás de uma instrução genérica do agente, como «encerre o processo da minha tarefa anterior». O agente deve receber um identificador opaco de execução. O supervisor transforma esse identificador em uma ação remota com escopo restrito. Esse desenho também torna a auditoria legível: a pessoa revisora vê que a execução 6c4... solicitou o cancelamento do PGID 24182 em um host, não que um agente montou um comando arbitrário de encerramento.
Para agentes autônomos de programação, o Sallyport pode executar ações SSH sem expor a chave SSH ao agente. Isso mantém a custódia da credencial separada do contrato de cancelamento, que ainda precisa do identificador da execução, da verificação do grupo, da concessão remota e do registro final.
A decisão correta de tentar novamente depende do efeito colateral
Um comando que apenas lê um arquivo geralmente pode ser repetido depois de uma desconexão com estado desconhecido. Um comando que cria um usuário, aplica uma migração, troca um certificado ou inicia uma implantação não pode. A camada SSH não consegue informar se a operação se tornou segura para repetir.
Dê aos comandos remotos com efeitos colaterais um token de idempotência derivado do ID da execução. O programa remoto deve armazenar o token junto do resultado da operação e devolver o resultado existente se vir o mesmo token novamente. Se isso for impossível, adicione uma consulta preliminar que identifique se a alteração solicitada já aconteceu.
Não use a limpeza como substituta da idempotência. Mesmo um TERM perfeito pode chegar depois que uma API remota aceitou uma solicitação, mas antes que o comando imprima sua resposta. Até um KILL pode chegar depois que uma transação de banco de dados foi confirmada. A limpeza de processos responde se o worker ainda está executando. Ela não desfaz efeitos externos.
Mantenha três resultados no plano de controle: concluído, cancelado com limpeza confirmada e desconhecido. Desconhecido é desconfortável, mas fornece à próxima pessoa operadora a informação necessária: inspecionar o estado remoto antes de emitir outra ação. Isso é muito melhor do que um botão verde de nova tentativa baseado em uma expectativa sem fundamento.
Um recurso de cancelamento está pronto quando você consegue interrompê-lo em cada limite e explicar a árvore de processos sobrevivente, a saída em disco, o registro final e a decisão de nova tentativa. Se não consegue fazer isso em um host descartável, não confie nele em um host de produção às 2 da manhã.
FAQ
Como interromper um comando SSH remoto quando um agente de IA o cancela?
Trate o cancelamento como uma ação de controle separada, não como a saída do cliente SSH local. Envie um sinal para um grupo de processos remoto registrado, aguarde um período de limpeza limitado e verifique o estado final da execução em um registro persistente. Se o caminho de controle já tiver desaparecido, uma concessão remota ou um watchdog deve decidir quando interromper o trabalho.
Fechar uma conexão SSH encerra o comando remoto?
Não. O fechamento de um canal SSH encerra o canal, mas não garante que o servidor enviará SIGTERM ao comando ou aos seus descendentes. Um PTY pode alterar o comportamento por meio de sinais de desligamento do terminal, mas isso ainda não é um contrato de limpeza no qual você deva confiar.
Como encerrar processos filhos iniciados por um script de shell remoto?
Use um grupo de processos ou uma sessão dedicada para cada execução remota e envie o sinal ao PGID negativo, por exemplo, kill -TERM -- -12345. Depois, verifique o PGID antes de depender dele. Encerrar apenas o PID do shell deixa para trás processos em segundo plano, pipelines e subprocessos.
Devo usar setsid em comandos remotos executados por agentes?
Uma chamada normal de setsid cria uma nova sessão e um grupo de processos para o comando iniciado, dando ao supervisor um único grupo para encerrar. Não use isso em trabalhos que devam sobreviver ao cancelamento. Verifique o SID e o PGID resultantes com ps, pois wrappers e gerenciadores de serviços podem alterar a árvore.
Como fazer a limpeza depois de uma desconexão SSH ou falha de rede?
Um processo remoto deve ter um tempo máximo de execução ou uma concessão renovável se deixá-lo rodando for inseguro ou caro. A concessão precisa existir no host remoto, porque não é possível confiar em um cliente desconectado para informar que desapareceu. Um sinal de cancelamento pode interromper o trabalho rapidamente, enquanto a concessão cobre o caso em que esse sinal nunca chega.
Posso confiar na saída parcial depois que a conexão SSH cai?
Use a saída do fluxo para observação, mas grave-a também em um arquivo ou diário remoto. Armazene um registro separado de conclusão somente depois que o comando terminar e a limpeza for concluída. Um fluxo interrompido informa que a entrega falhou, não se o comando terminou.
PR_SET_PDEATHSIG é suficiente para limpar processos remotos?
Ele é específico do Linux e apenas ajuda um processo a perceber que seu pai direto morreu. PR_SET_PDEATHSIG não cobre automaticamente os netos, e mudanças de privilégio podem limpar essa configuração. Use-o como reforço local dentro de um worker criado para esse fim, não como o único mecanismo de parada para comandos arbitrários.
Um agente deve alocar um PTY para comandos SSH remotos?
Geralmente, não. Um terminal altera a entrega de sinais, o buffering e o comportamento do programa, enquanto um comando não interativo produz uma saída de máquina mais limpa. Alocque um PTY apenas quando o programa remoto realmente precisar de semântica de terminal e teste o comportamento de desligamento separadamente.
O que deve conter um registro de auditoria de cancelamento SSH?
Registre um ID de execução aleatório, a identidade do host remoto, o PID inicial, o PGID, o SID, o horário de início, o comando solicitado e o resultado final. Proteja o registro remoto contra outros usuários e torne atômica a atualização do status final. Um PID sozinho é uma evidência fraca, pois os PIDs podem ser reutilizados.
Um agente pode executar trabalho destrutivo com segurança por SSH?
Não envie um comando SSH sem limite e não chame o trabalho de cancelado apenas porque o processo local terminou. Exija um identificador de execução, uma verificação do grupo de processos, um prazo de limpeza e um status remoto final. O Sallyport pode manter a credencial SSH fora do agente, mas o desenho do comando remoto ainda determina se o cancelamento é honesto.