# `signal` — Set handlers for asynchronous events

Código-fonte: Lib/signal.py

Este módulo fornece mecanismos para usar manipuladores de sinal em Python.

## Regras gerais

A função `signal.signal()` permite definir manipuladores personalizados a serem executados quando um sinal é recebido. Um pequeno número de manipuladores padrão são instalados: `SIGPIPE` é ignorado (então erros de gravação em encadeamentos e sockets podem ser relatados como exceções comuns do Python) e `SIGINT` é traduzido em uma exceção `KeyboardInterrupt` se o processo pai não a tiver alterado.

Um manipulador para um sinal específico, uma vez definido, permanece instalado até ser explicitamente redefinido (o Python emula a interface de estilo BSD independentemente da implementação subjacente), com exceção do manipulador para `SIGCHLD`, que segue a implementação subjacente.

### Execução de manipuladores de sinais Python

Um manipulador de sinal Python não é executado dentro do manipulador de sinal de baixo nível (C). Em vez disso, o manipulador de sinal de baixo nível define um sinalizador que diz à máquina virtual para executar o manipulador de sinal Python correspondente em um ponto posterior (por exemplo, na próxima instrução bytecode). Isso tem consequências:

- Faz pouco sentido capturar erros síncronos como `SIGFPE` ou `SIGSEGV` que são causados por uma operação inválida no código C. O Python retornará do manipulador de sinais para o código C, o que provavelmente levantará o mesmo sinal novamente, fazendo com que o Python aparentemente trave. Do Python 3.3 em diante, você pode usar o módulo `faulthandler` para relatar erros síncronos.

- Um cálculo de longa duração implementado puramente em C (como correspondência de expressão regular em um grande corpo de texto) pode ser executado ininterruptamente por um período de tempo arbitrário, independentemente de quaisquer sinais recebidos. Os manipuladores de sinal do Python serão chamados quando o cálculo terminar.

- Se o manipulador levantar uma exceção, ela será levantada “do nada” na thread principal. Veja a nota abaixo para uma discussão.

### Sinais e threads

Os manipuladores de sinais Python são sempre executados na thread principal do Python do interpretador principal, mesmo se o sinal foi recebido em outra thread. Isso significa que os sinais não podem ser usados como um meio de comunicação entre threads. Você pode usar as primitivas de sincronização do módulo `threading` em vez disso.

Além disso, somente a thread principal do interpretador principal tem permissão para definir um novo manipulador de sinal.

## Conteúdo do módulo

Alterado na versão 3.5: signal (SIG*), handler (`SIG_DFL`, `SIG_IGN`) and sigmask (`SIG_BLOCK`, `SIG_UNBLOCK`, `SIG_SETMASK`) related constants listed below were turned into `enums`. `getsignal()`, `pthread_sigmask()`, `sigpending()` and `sigwait()` functions return human-readable `enums`.

As variáveis definidas no módulo `signal` são:

`signal.``SIG_DFL`

  Esta é uma das duas opções de manipulação de sinal padrão; ela simplesmente executará a função padrão para o sinal. Por exemplo, na maioria dos sistemas, a ação padrão para `SIGQUIT` é despejar o núcleo e sair, enquanto a ação padrão para `SIGCHLD` é simplesmente ignorá-lo.

`signal.``SIG_IGN`

  Este é outro manipulador de sinal padrão, que simplesmente ignorará o sinal fornecido.

`signal.``SIGABRT`

  Sinal de abortar de abort(3).

`signal.``SIGALRM`

  Sinal do temporizador de alarm(2).

  Disponibilidade: Unix.

`signal.``SIGBREAK`

  Interrupção do teclado (CTRL + BREAK).

  Disponibilidade: Windows.

`signal.``SIGBUS`

  Erro de barramento (acesso incorreto à memória).

  Disponibilidade: Unix.

`signal.``SIGCHLD`

  Processo filho interrompido ou encerrado.

  Disponibilidade: Unix.

`signal.``SIGCLD`

  Apelido para `SIGCHLD`.

`signal.``SIGCONT`

  Continua o processo se ele estiver parado no momento

  Disponibilidade: Unix.

`signal.``SIGFPE`

  Exceção de ponto flutuante. Por exemplo, divisão por zero.

  Ver também

  `ZeroDivisionError` é levatanda quando o segundo argumento de uma operação de divisão ou módulo é zero.

`signal.``SIGHUP`

  Travamento detectado no terminal de controle ou morte do processo de controle.

  Disponibilidade: Unix.

`signal.``SIGILL`

  Instrução ilegal.

`signal.``SIGINT`

  Interrupção do teclado (CTRL + C).

  A ação padrão é levantar `KeyboardInterrupt`.

`signal.``SIGKILL`

  Sinal para encerrar forçadamente.

  Ele não pode ser capturado, bloqueado ou ignorado.

  Disponibilidade: Unix.

`signal.``SIGPIPE`

  Encadeamento quebrado: escreve no encadeamento sem leitores.

  A ação padrão é ignorar o sinal.

  Disponibilidade: Unix.

`signal.``SIGSEGV`

  Falha de segmentação: referência de memória inválida.

`signal.``SIGTERM`

  Sinal de encerramento.

`signal.``SIGUSR1`

  Sinal definido pelo usuário 1.

  Disponibilidade: Unix.

`signal.``SIGUSR2`

  Sinal definido pelo usuário 2.

  Disponibilidade: Unix.

`signal.``SIGWINCH`

  Sinal de redimensionamento do Windows.

  Disponibilidade: Unix.

`SIG*`

  Todos os números de sinal são definidos simbolicamente. Por exemplo, o sinal de desligamento é definido como `signal.SIGHUP`; os nomes das variáveis são idênticos aos usados em programas C, como encontrados em `<signal.h>`. A página man do Unix para ‘`signal()`’ lista os sinais existentes (em alguns sistemas, é signal(2), em outros, a lista está em signal(7)). Observe que nem todos os sistemas definem o mesmo conjunto de nomes de sinais; apenas os nomes definidos pelo sistema são definidos por este módulo.

`signal.``CTRL_C_EVENT`

  O sinal correspondente ao evento de pressionamento de tecla ``Ctrl`+`C``. Este sinal só pode ser usado com `os.kill()`.

  Disponibilidade: Windows.

  Novo na versão 3.2.

`signal.``CTRL_BREAK_EVENT`

  O sinal correspondente ao evento de pressionamento de tecla ``Ctrl`+`Break``. Este sinal só pode ser usado com `os.kill()`.

  Disponibilidade: Windows.

  Novo na versão 3.2.

`signal.``NSIG`

  One more than the number of the highest signal number.

`signal.``ITIMER_REAL`

  Diminui o intervalo do temporizador em tempo real e entrega `SIGALRM` ao expirar.

`signal.``ITIMER_VIRTUAL`

  Diminui o intervalo do temporizador somente quando o processo está em execução e entrega SIGVTALRM após a expiração.

`signal.``ITIMER_PROF`

  Diminui o temporizador de intervalo tanto quando o processo é executado quanto quando o sistema está executando em nome do processo. Juntamente com o ITIMER_VIRTUAL, este temporizador é geralmente usado para criar um perfil do tempo gasto pelo aplicativo nos espaços do usuário e do kernel. O SIGPROF é fornecido após a expiração.

`signal.``SIG_BLOCK`

  Um possível valor para o parâmetro how para `pthread_sigmask()` indicando que os sinais devem ser bloqueados.

  Novo na versão 3.3.

`signal.``SIG_UNBLOCK`

  Um possível valor para o parâmetro how para `pthread_sigmask()` indicando que os sinais devem ser desbloqueados.

  Novo na versão 3.3.

`signal.``SIG_SETMASK`

  Um possível valor para o parâmetro how para `pthread_sigmask()` indicando que a máscara de sinal deve ser substituída.

  Novo na versão 3.3.

O módulo `signal` define uma exceção:

exception `signal.``ItimerError`

  Levantada para sinalizar um erro da implementação subjacente de `setitimer()` ou `getitimer()`. Espere este erro se um temporizador de intervalo inválido ou um tempo negativo for passado para `setitimer()`. Este erro é um subtipo de `OSError`.

  Novo na versão 3.3: Este erro costumava ser um subtipo de `IOError`, que agora é um apelido de `OSError`.

O módulo `signal` define as seguintes funções:

`signal.``alarm`(time)

  Se time for diferente de zero, esta função solicita que um sinal `SIGALRM` seja enviado ao processo em time segundos. Qualquer alarme previamente agendado será cancelado (apenas um alarme pode ser agendado por vez). O valor retornado será o número de segundos antes de qualquer alarme previamente configurado ter sido emitido. Se time for zero, nenhum alarme será agendado e qualquer alarme agendado será cancelado. Se o valor retornado for zero, nenhum alarme será agendado no momento.

  Availability: Unix. See the man page alarm(2) for further information.

`signal.``getsignal`(signalnum)

  Retorna o manipulador de sinal atual para o sinal signalnum. O valor retornado pode ser um objeto Python invocável ou um dos valores especiais `signal.SIG_IGN`, `signal.SIG_DFL` ou `None`. Aqui, `signal.SIG_IGN` significa que o sinal foi ignorado anteriormente, `signal.SIG_DFL` significa que a maneira padrão de manipular o sinal estava em uso anteriormente e `None` significa que o manipulador de sinal anterior não foi instalado a partir do Python.

`signal.``strsignal`(signalnum)

  Return the system description of the signal signalnum, such as “Interrupt”, “Segmentation fault”, etc. Returns `None` if the signal is not recognized.

  Novo na versão 3.8.

`signal.``valid_signals`()

  Retorna o conjunto de números de sinais válidos nesta plataforma. Pode ser menor que `range(1, NSIG)` se alguns sinais forem reservados pelo sistema para uso interno.

  Novo na versão 3.8.

`signal.``pause`()

  Faz o processo hibernar até que um sinal seja recebido; o manipulador apropriado será então chamado. Não retorna nada.

  Availability: Unix. See the man page signal(2) for further information.

  veja também `sigwait()`, `sigwaitinfo()`, `sigtimedwait()` e `sigpending()`.

`signal.``raise_signal`(signum)

  Envia um sinal para o processo de chamada. Não retorna nada.

  Novo na versão 3.8.

`signal.``pidfd_send_signal`(pidfd, sig, siginfo=None, flags=0)

  Envia o sinal sig para o processo referenciado pelo descritor de arquivo pidfd. Atualmente, o Python não suporta o parâmetro siginfo; ele deve ser `None`. O argumento flags é fornecido para futuras extensões; nenhum valor de sinalizador está definido no momento.

  Veja a página man pidfd_send_signal(2) para mais informações.

  Availability: Linux 5.1+

  Novo na versão 3.9.

`signal.``pthread_kill`(thread_id, signalnum)

  Envia o sinal signalnum para a thread thread_id, outra thread no mesmo processo que a chamadora. A thread alvo pode estar executando qualquer código (Python ou não). No entanto, se a thread alvo estiver executando o interpretador Python, os manipuladores de sinal Python serão executados pela thread principal do interpretador principal. Portanto, o único objetivo de enviar um sinal para uma thread Python específica seria forçar uma chamada de sistema em execução a falhar com `InterruptedError`.

  Use `threading.get_ident()` or the `ident` attribute of `threading.Thread` objects to get a suitable value for thread_id.

  Se signalnum for 0, nenhum sinal será enviado, mas a verificação de erros ainda será realizada; isso pode ser usado para verificar se a thread de destino ainda está em execução.

  Levanta um evento de auditoria `signal.pthread_kill` com os argumentos `thread_id`, `signalnum`.

  Availability: Unix. See the man page pthread_kill(3) for further information.

  Veja também `os.kill()`.

  Novo na versão 3.3.

`signal.``pthread_sigmask`(how, mask)

  Busca e/ou altera a máscara de sinal da thread chamadora. A máscara de sinal é o conjunto de sinais cuja entrega está atualmente bloqueada para o chamador. Retorna a máscara de sinal antiga como um conjunto de sinais.

  O comportamento da chamada depende do valor de how, como segue.

  - `SIG_BLOCK`: O conjunto de sinais bloqueados é a união do conjunto atual e do argumento mask.

  - `SIG_UNBLOCK`: Os sinais em mask são removidos do conjunto atual de sinais bloqueados. É permitido tentar desbloquear um sinal que não esteja bloqueado.

  - `SIG_SETMASK`: O conjunto de sinais bloqueados é definido como o argumento mask.

  mask é um conjunto de números de sinais (ex.: {`signal.SIGINT`, `signal.SIGTERM`}). Use `valid_signals()` para uma máscara completa, incluindo todos os sinais.

  Por exemplo, `signal.pthread_sigmask(signal.SIG_BLOCK, [])` lê a máscara de sinal da thread de chamada.

  `SIGKILL` e `SIGSTOP` não podem ser bloqueados.

  Availability: Unix. See the man page sigprocmask(2) and pthread_sigmask(3) for further information.

  Veja também `pause()`, `sigpending()` e `sigwait()`.

  Novo na versão 3.3.

`signal.``setitimer`(which, seconds, interval=0.0)

  Define o temporizador de intervalo fornecido (um dos seguintes: `signal.ITIMER_REAL`, `signal.ITIMER_VIRTUAL` ou `signal.ITIMER_PROF`) especificado por which para disparar após seconds (float é aceito, diferente de `alarm()`) e, depois disso, a cada interval segundos (se interval for diferente de zero). O temporizador de intervalo especificado por which pode ser zerado definindo seconds como zero.

  Quando um temporizador de intervalo dispara, um sinal é enviado ao processo. O sinal enviado depende do temporizador utilizado; `signal.ITIMER_REAL` enviará `SIGALRM`, `signal.ITIMER_VIRTUAL` enviará `SIGVTALRM` e `signal.ITIMER_PROF` enviará `SIGPROF`.

  Os valores antigos são retornados como uma tupla: (atraso, intervalo).

  Tentar passar um intervalo de tempo inválido causará uma `ItimerError`.

  Disponibilidade: Unix.

`signal.``getitimer`(which)

  Retorna o valor atual de um intervalo de tempo especificado por which.

  Disponibilidade: Unix.

`signal.``set_wakeup_fd`(fd, *, warn_on_full_buffer=True)

  Define o descritor de arquivo de ativação como fd. Quando um sinal é recebido, o número do sinal é escrito como um único byte no fd. Isso pode ser usado por uma biblioteca para ativar uma chamada de poll ou seleção, permitindo que o sinal seja totalmente processado.

  O antigo fd de ativação é retornado (ou -1 se a ativação do descritor de arquivo não estava habilitada). Se fd for -1, a ativação do descritor de arquivo está desabilitada. Se não for -1, fd deve ser não bloqueante. Cabe à biblioteca remover quaisquer bytes de fd antes de chamar poll ou select novamente.

  Quando threads estão habilitadas, esta função só pode ser chamada da thread principal do interpretador principal; tentar chamá-la de outras threads levantará uma exceção `ValueError`.

  Há duas maneiras comuns de usar esta função. Em ambas as abordagens, você usa o fd para despertar quando um sinal chega, mas elas diferem na forma como determinam which sinal ou sinais chegaram.

  Na primeira abordagem, lemos os dados do buffer do fd, e os valores dos bytes fornecem os números dos sinais. Isso é simples, mas em casos raros pode apresentar um problema: geralmente, o fd terá um espaço de buffer limitado e, se muitos sinais chegarem muito rápido, o buffer pode ficar cheio e alguns sinais podem ser perdidos. Se você usar essa abordagem, defina `warn_on_full_buffer=True`, o que pelo menos fará com que um aviso seja impresso no stderr quando os sinais forem perdidos.

  Na segunda abordagem, usamos o fd de ativação apenas para ativações e ignoramos os valores de bytes reais. Nesse caso, tudo o que importa é se o buffer do fd está vazio ou não; um buffer cheio não indica nenhum problema. Se você usar essa abordagem, defina `warn_on_full_buffer=False` para que seus usuários não sejam confundidos por mensagens de aviso falsas.

  Alterado na versão 3.5: No Windows, a função agora também suporta manipuladores de socket.

  Alterado na versão 3.7: Adiciona o parâmetro `warn_on_full_buffer`.

`signal.``siginterrupt`(signalnum, flag)

  Altera o comportamento de reinicialização de chamadas de sistema: se flag for `False`, as chamadas de sistema serão reiniciadas quando interrompidas pelo sinal signalnum; caso contrário, as chamadas de sistema serão interrompidas. Não retorna nada.

  Availability: Unix. See the man page siginterrupt(3) for further information.

  Note that installing a signal handler with `signal()` will reset the restart behaviour to interruptible by implicitly calling `siginterrupt()` with a true flag value for the given signal.

`signal.``signal`(signalnum, handler)

  Define o manipulador do sinal signalnum para a função handler. handler pode ser um objeto Python invocável que recebe dois argumentos (veja abaixo), ou um dos valores especiais `signal.SIG_IGN` ou `signal.SIG_DFL`. O manipulador de sinal anterior será retornado (veja a descrição de `getsignal()` acima). (Consulte a página man do Unix signal(2) para mais informações.)

  Quando threads estão habilitadas, esta função só pode ser chamada da thread principal do interpretador principal; tentar chamá-la de outras threads levantará uma exceção `ValueError`.

  O handler é chamado com dois argumentos: o número do sinal e o quadro de pilha atual (`None` ou um objeto quadro; para uma descrição dos objetos quadro, consulte a descrição na hierarquia de tipo ou consulte as descrições de atributo no módulo `inspect`).

  No Windows, `signal()` só pode ser chamado com `SIGABRT`, `SIGFPE`, `SIGILL`, `SIGINT`, `SIGSEGV`, `SIGTERM` ou `SIGBREAK`. Uma exceção `ValueError` será levantada em qualquer outro caso. Observe que nem todos os sistemas definem o mesmo conjunto de nomes de sinais; uma exceção `AttributeError` será levantada se um nome de sinal não for definido como constante de nível de módulo `SIG*`.

`signal.``sigpending`()

  Examine o conjunto de sinais pendentes para entrega ao thread de chamada (ou seja, os sinais que foram gerados enquanto bloqueados). Retorne o conjunto de sinais pendentes.

  Availability: Unix. See the man page sigpending(2) for further information.

  Veja também `pause()`, `pthread_sigmask()` e `sigwait()`.

  Novo na versão 3.3.

`signal.``sigwait`(sigset)

  Suspende a execução do thread de chamada até a entrega de um dos sinais especificados no conjunto de sinais sigset. A função aceita o sinal (remove-o da lista de sinais pendentes) e retorna o número do sinal.

  Availability: Unix. See the man page sigwait(3) for further information.

  Veja também `pause()`, `pthread_sigmask()`, `sigpending()`, `sigwaitinfo()` e `sigtimedwait()`.

  Novo na versão 3.3.

`signal.``sigwaitinfo`(sigset)

  Suspende a execução da thread chamadora até a entrega de um dos sinais especificados no conjunto de sinais sigset. A função aceita o sinal e o remove da lista de sinais pendentes. Se um dos sinais em sigset já estiver pendente para a thread chamadora, a função retornará imediatamente com informações sobre esse sinal. O manipulador de sinais não é chamado para o sinal entregue. A função levanta `InterruptedError` se for interrompida por um sinal que não esteja em sigset.

  O valor de retorno é um objeto que representa os dados contidos na estrutura de `siginfo_t`, a saber: `si_signo`, `si_code`, `si_errno`, `si_pid`, `si_uid`, `si_status`, `si_band`.

  Availability: Unix. See the man page sigwaitinfo(2) for further information.

  Veja também `pause()`, `sigwait()` e `sigtimedwait()`.

  Novo na versão 3.3.

  Alterado na versão 3.5: A função agora é tentada novamente se interrompida por um sinal que não esteja em sigset e o manipulador de sinal não levanta uma exceção (veja PEP 475 para a justificativa).

`signal.``sigtimedwait`(sigset, timeout)

  Like `sigwaitinfo()`, but takes an additional timeout argument specifying a timeout. If timeout is specified as `0`, a poll is performed. Returns `None` if a timeout occurs.

  Availability: Unix. See the man page sigtimedwait(2) for further information.

  Veja também `pause()`, `sigwait()` e `sigwaitinfo()`.

  Novo na versão 3.3.

  Alterado na versão 3.5: A função agora é tentada novamente com o timeout recalculado se interrompida por um sinal que não esteja em sigset e o manipulador de sinal não levanta uma exceção (veja PEP 475 para a justificativa).

## Exemplo

Aqui está um programa de exemplo mínimo. Ele usa a função `alarm()` para limitar o tempo gasto esperando para abrir um arquivo; isso é útil se o arquivo for para um dispositivo serial que pode não estar ligado, o que normalmente faria com que o `os.open()` travasse indefinidamente. A solução é definir um alarme de 5 segundos antes de abrir o arquivo; se a operação demorar muito, o sinal de alarme será enviado e o manipulador levantará uma exceção.

    import signal, os

    def handler(signum, frame):
        print('Signal handler called with signal', signum)
        raise OSError("Couldn't open device!")

    # Set the signal handler and a 5-second alarm
    signal.signal(signal.SIGALRM, handler)
    signal.alarm(5)

    # This open() may hang indefinitely
    fd = os.open('/dev/ttyS0', os.O_RDWR)

    signal.alarm(0)          # Disable the alarm

## Nota sobre SIGPIPE

Canalizar a saída do seu programa para ferramentas como head(1) fará com que um sinal `SIGPIPE` seja enviado ao seu processo quando o receptor da saída padrão fechar antes do tempo. Isso resulta em uma exceção como `BrokenPipeError: [Errno 32] Broken pipe`. Para lidar com esse caso, envolva seu ponto de entrada para capturar essa exceção da seguinte maneira:

    import os
    import sys

    def main():
        try:
            # simulate large output (your code replaces this loop)
            for x in range(10000):
                print("y")
            # flush output here to force SIGPIPE to be triggered
            # while inside this try block.
            sys.stdout.flush()
        except BrokenPipeError:
            # Python flushes standard streams on exit; redirect remaining output
            # to devnull to avoid another BrokenPipeError at shutdown
            devnull = os.open(os.devnull, os.O_WRONLY)
            os.dup2(devnull, sys.stdout.fileno())
            sys.exit(1)  # Python exits with error code 1 on EPIPE

    if __name__ == '__main__':
        main()

Não defina a disposição de `SIGPIPE` como `SIG_DFL` para evitar `BrokenPipeError`. Isso faria com que seu programa encerrasse inesperadamente sempre que qualquer conexão de soquete fosse interrompida enquanto o programa ainda estivesse escrevendo nele.

## Nota sobre manipuladores de sinais e exceções

Se um manipulador de sinais levantar uma exceção, a exceção será propagada para a thread principal e poderá ser levantada após qualquer instrução bytecode. Mais notavelmente, uma `KeyboardInterrupt` pode aparecer a qualquer momento durante a execução. A maioria dos códigos Python, incluindo a biblioteca padrão, não pode ser robusta contra isso, e, portanto, uma `KeyboardInterrupt` (ou qualquer outra exceção resultante de um manipulador de sinais) pode, em raras ocasiões, colocar o programa em um estado inesperado.

Para ilustrar esse problema, considere o seguinte código:

    class SpamContext:
        def __init__(self):
            self.lock = threading.Lock()

        def __enter__(self):
            # If KeyboardInterrupt occurs here, everything is fine
            self.lock.acquire()
            # If KeyboardInterrupt occurs here, __exit__ will not be called
            ...
            # KeyboardInterrupt could occur just before the function returns

        def __exit__(self, exc_type, exc_val, exc_tb):
            ...
            self.lock.release()

Para muitos programas, especialmente aqueles que simplesmente desejam encerrar em `KeyboardInterrupt`, isso não é um problema, mas aplicações complexos ou que exigem alta confiabilidade devem evitar levantar exceções de manipuladores de sinal. Eles também devem evitar capturar `KeyboardInterrupt` como forma de encerrar o programa sem problemas. Em vez disso, devem instalar seu próprio manipulador `SIGINT`. Abaixo está um exemplo de um servidor HTTP que evita `KeyboardInterrupt`:

    import signal
    import socket
    from selectors import DefaultSelector, EVENT_READ
    from http.server import HTTPServer, SimpleHTTPRequestHandler

    interrupt_read, interrupt_write = socket.socketpair()

    def handler(signum, frame):
        print('Signal handler called with signal', signum)
        interrupt_write.send(b'\0')
    signal.signal(signal.SIGINT, handler)

    def serve_forever(httpd):
        sel = DefaultSelector()
        sel.register(interrupt_read, EVENT_READ)
        sel.register(httpd, EVENT_READ)

        while True:
            for key, _ in sel.select():
                if key.fileobj == interrupt_read:
                    interrupt_read.recv(1)
                    return
                if key.fileobj == httpd:
                    httpd.handle_request()

    print("Serving on port 8000")
    httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
    serve_forever(httpd)
    print("Shutdown...")