Como executar um longo processo no evento Udev?

11

Quero executar uma conexão ppp quando meu modem USB estiver conectado, portanto, uso esta udevregra:

ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="16d8",\
    RUN+="/usr/local/bin/newPPP.sh $env{DEVNAME}"

(Meu modem aparece /devcomo ttyACM0)

newPPP.sh:

#!/bin/bash
/usr/bin/pon prov $1 >/dev/null 2>&1 &

Problema:

O udevevento é acionado e o newPPP.sh está em execução, mas o newPPP.shprocesso é interrompido após ~ 4-5s. pppnão tem tempo para se conectar (o tempo limite é 10s para discagem).

Como posso executar um processo de longo prazo, que não será morto?

Eu tentei usar nohup, mas também não funcionou.

Sistema: Arch Linux

Atualizar

Encontrei uma solução aqui , graças ao maxschlepzig .

Eu uso at nowpara executar meu trabalho desanexado do processo udev.

Mas a única pergunta permanece sem resposta: por que funciona nohupe &não funciona?

Comunidade
fonte

Respostas:

11

Se você executar uma distribuição decente com suporte ao systemd, a maneira mais fácil e tecnicamente mais segura é usar uma unidade de dispositivo .

Dessa forma, o systemd estará no controle total do script de execução demorada e até poderá finalizar o processo corretamente assim que o dispositivo for desligado / removido - desanexar o processo significa que você está desistindo de estar no controle total do estado do processo e sua história.

Além disso, você poderá inspecionar o status do dispositivo e do serviço conectado executando systemctl status my-ppp-thing.device.

Veja também esta postagem no blog para mais alguns exemplos e detalhes.

Elias Probst
fonte
6

Atualmente, o udev usa o cgroups para procurar e gerar tarefas geradas. Uma solução é usar "at now" ou "batch". Outra solução é fazer o fork duplo e "realocar" o processo para outro cgroup. Este é um exemplo de código python (código semelhante pode ser escrito em qualquer idioma):

os.closerange(0, 65535)  # just in case
pid = os.fork()
if not pid:
  pid = os.fork()  # fork again so the child would adopted by init
  if not pid:
    # relocate this process to another cgroup
    with open("/sys/fs/cgroup/cpu/tasks", "a+") as fd:
      fd.write(str(os.getpid()))
    sleep(3)  # defer execution by XX seconds
    # YOUR CODE GOES HERE
sleep(0.1)  # get forked process chance to change cgroup

A saída de depuração pode ser enviada para, por exemplo, syslog.

Alexandre Kandalintsev
fonte
1
Por que o udev se esforçaria tanto para destruir os processos gerados?
User30747
Eu acho que é porque os programas iniciados pelo udev bloqueiam o daemon (por exemplo, com uma regra do udev conectada à conexão de um monitor externo, um programa de longa duração impedirá que o novo monitor seja realmente usado). Tenho certeza de que possui seu próprio raciocínio técnico, mas significa que os processos gerados podem sustentar grandes partes do sistema e precisam ser mortos.
tobek
2

O Shell tem capacidade de executar comandos em segundo plano:

(

lots of code

) &

Os comandos agrupados pelos chavetas com e comercial depois deles serão executados de forma assíncrona em um subshell. Eu uso isso para conectar automaticamente quando um modem USB é inserido e comutado. Demora cerca de 20 segundos e funciona bem no udev.

user42295
fonte
Você pode redirecionar stderr, stdout e stderr em tal situação.
Mdpc
@mdpc hmm ... por quê? Vi fecha usb_modeswitch córregos neste cenário: exec 1 <& - 2 <& - 5 <& - 7 <& -
user42295
1

Eu consegui trabalhar com o setsid. Minha parte RUN da regra do udev:

RUN+="/bin/bash script.sh"

então no script:

#!/bin/bash
if [ "$1" != "fo_real" ]; then
  /usr/bin/setsid $(/usr/bin/dirname $0)/$(/usr/bin/basename $0) fo_real &
  exit
fi

Rest of script is here....

A primeira chamada para o script retorna com o status de saída 0, mas a segunda chamada para o script continua em execução com PPID = 1.

Vivo
fonte
0

Provavelmente porque seu processo pai é finalizado e o sinal de finalização se propaga para seus filhos, que não o bloqueiam (e, no caso de SIGKILLeles nem conseguirem).

peterph
fonte