Bagaimana cara membuat daemonisasi skrip arbitrer di unix?

93

Saya ingin daemonizer yang dapat mengubah skrip atau perintah umum yang berubah-ubah menjadi daemon .

Ada dua kasus umum yang ingin saya tangani:

  1. Saya memiliki skrip yang harus berjalan selamanya. Jika pernah mati (atau saat reboot), mulai ulang. Jangan biarkan ada dua salinan yang berjalan sekaligus (deteksi jika salinan sudah berjalan dan jangan luncurkan dalam kasus itu).

  2. Saya memiliki skrip sederhana atau perintah baris perintah yang ingin terus saya jalankan berulang kali selamanya (dengan jeda singkat di antara proses). Sekali lagi, jangan izinkan dua salinan skrip untuk dijalankan sekaligus.

Tentu saja sepele untuk menulis loop "while (true)" di sekitar skrip pada kasus 2 dan kemudian menerapkan solusi untuk kasus 1, tetapi solusi yang lebih umum hanya akan menyelesaikan kasus 2 secara langsung karena itu berlaku untuk skrip dalam kasus 1 sebagai baik (Anda mungkin hanya ingin jeda yang lebih pendek atau tidak ada jeda jika skrip tidak dimaksudkan untuk mati (tentu saja jika skrip benar - benar tidak pernah mati maka jeda sebenarnya tidak masalah)).

Perhatikan bahwa solusinya tidak boleh melibatkan, katakanlah, menambahkan kode penguncian file atau rekaman PID ke skrip yang ada.

Lebih khusus lagi, saya ingin program "daemonize" yang dapat saya jalankan

% daemonize myscript arg1 arg2

atau, misalnya,

% daemonize 'echo `date` >> /tmp/times.txt'

yang akan membuat daftar tanggal bertambah ditambahkan ke times.txt. (Perhatikan bahwa jika argumen untuk daemonize adalah skrip yang berjalan selamanya seperti pada kasus 1 di atas, maka daemonize akan tetap melakukan hal yang benar, memulai ulang bila perlu.) Saya kemudian dapat meletakkan perintah seperti di atas di .login saya dan / atau cron setiap jam atau menit (tergantung seberapa khawatir saya tentang hal itu meninggal secara tidak terduga).

NB: Skrip daemonize perlu mengingat string perintah yang di-daemonisasi sehingga jika string perintah yang sama di-daemonisasi lagi, salinan kedua tidak akan diluncurkan.

Selain itu, solusi idealnya harus berfungsi pada OS X dan linux tetapi solusi untuk satu atau yang lain dipersilakan.

EDIT: Tidak apa-apa jika Anda harus memintanya dengan sudo daemonize myscript myargs.

(Jika saya memikirkan ini semua salah atau ada solusi parsial yang cepat dan kotor, saya ingin mendengarnya juga.)


PS: Jika bermanfaat, berikut pertanyaan serupa khusus untuk python.

Dan ini jawaban untuk pertanyaan serupa memiliki apa yang tampaknya menjadi idiom yang berguna untuk mengutuk cepat-dan-kotor dari skrip yang sewenang-wenang:

dreeves
sumber
1
Lihat serverfault.com/questions/311593/… untuk versi shell murni
w00t

Jawaban:

90

Anda dapat membuat daemonisasi yang dapat dieksekusi di Unix dengan menggunakan nohup dan operator &:

nohup yourScript.sh script args&

Perintah nohup memungkinkan Anda untuk menutup sesi shell Anda tanpa mematikan skrip Anda, sementara & menempatkan skrip Anda di latar belakang sehingga Anda mendapatkan prompt shell untuk melanjutkan sesi Anda. Satu-satunya masalah kecil dengan ini adalah standar keluar dan kesalahan standar keduanya dikirim ke ./nohup.out, jadi jika Anda memulai beberapa skrip dalam manor ini, hasilnya akan saling terkait. Perintah yang lebih baik adalah:

nohup yourScript.sh script args >script.out 2>script.error&

Ini akan mengirimkan standar ke file pilihan Anda dan kesalahan standar ke file lain pilihan Anda. Jika Anda ingin menggunakan hanya satu file untuk standard out dan standard error, Anda dapat menggunakan ini:

nohup yourScript.sh script args >script.out 2>&1 &

2> & 1 memberi tahu shell untuk mengalihkan kesalahan standar (deskriptor file 2) ke file yang sama sebagai standar keluar (deskriptor file 1).

Untuk menjalankan perintah hanya sekali dan memulai ulang jika mati, Anda dapat menggunakan skrip ini:

#!/bin/bash

if [[ $# < 1 ]]; then
    echo "Name of pid file not given."
    exit
fi

# Get the pid file's name.
PIDFILE=$1
shift

if [[ $# < 1 ]]; then
    echo "No command given."
    exit
fi

echo "Checking pid in file $PIDFILE."

#Check to see if process running.
PID=$(cat $PIDFILE 2>/dev/null)
if [[ $? = 0 ]]; then
    ps -p $PID >/dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo "Command $1 already running."
        exit
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

# Get command.
COMMAND=$1
shift

# Run command until we're killed.
while true; do
    $COMMAND "$@"
    sleep 10 # if command dies immediately, don't go into un-ctrl-c-able loop
done

Argumen pertama adalah nama file pid yang akan digunakan. Argumen kedua adalah perintah. Dan semua argumen lainnya adalah argumen perintah.

Jika Anda menamai skrip ini restart.sh ini adalah bagaimana Anda menyebutnya:

nohup restart.sh pidFileName yourScript.sh script args >script.out 2>&1 &
Robert Menteer
sumber
Luar biasa; Terima kasih. Saya ingin tahu apakah itu juga harus memiliki opsi untuk penundaan dalam memulai kembali. Atau mungkin lebih baik hanya menggunakannya bersama dengan ini: stackoverflow.com/questions/555116/…
dreeves
4
Ini hanya menangani SIGHUP, ada sinyal fatal (biasanya) lainnya yang harus ditangani.
Tim Post
Cara lain untuk memperbaiki skrip ini mungkin dengan membuatnya memiliki lokasi yang bagus untuk menempatkan $ PIDFILE sendiri daripada mengharuskannya ditentukan sebagai arg. Ia bahkan tidak membersihkan dirinya sendiri! (yang seharusnya langsung dengan a trap EXIT)
Steven Lu
Juga, pertimbangkan bahwa penggunaan <dalam testadalah perbandingan ASCII, bukan perbandingan bilangan bulat. Ini mungkin masih berfungsi, tetapi dapat menyebabkan bug.
Steven Lu
Saya telah memposting perbaikan saya ke skrip ini di sini .
Steven Lu
34

Saya minta maaf untuk jawaban yang panjang (silakan lihat komentar tentang bagaimana jawaban saya memenuhi spesifikasi). Saya mencoba untuk menjadi komprehensif, jadi Anda memiliki kaki sebaik mungkin. :-)

Jika Anda dapat menginstal program (memiliki akses root), dan bersedia melakukan kerja keras satu kali untuk menyiapkan skrip Anda untuk eksekusi daemon (yaitu, lebih terlibat daripada sekadar menentukan argumen baris perintah untuk dijalankan pada baris perintah, tetapi hanya perlu dilakukan sekali per layanan), saya memiliki cara yang lebih kuat.

Ini melibatkan penggunaan daemontools . Bagian selanjutnya dari posting ini menjelaskan cara menyiapkan layanan menggunakan daemontools.

Pengaturan awal

  1. Ikuti petunjuk di Cara menginstal daemontools . Beberapa distribusi (misalnya Debian, Ubuntu) sudah memiliki paket untuk itu, jadi gunakan saja.
  2. Buat direktori bernama /service. Penginstal seharusnya sudah melakukan ini, tetapi verifikasi saja, atau jika menginstal secara manual. Jika Anda tidak menyukai lokasi ini, Anda dapat mengubahnya di svscanbootskrip Anda , meskipun sebagian besar pengguna daemontools biasa menggunakannya /servicedan akan bingung jika Anda tidak menggunakannya.
  3. Jika Anda menggunakan Ubuntu atau distro lain yang tidak menggunakan standar init(yaitu, tidak menggunakan /etc/inittab), Anda perlu menggunakan pra-instal inittabsebagai dasar untuk mengatur svscanbootagar dipanggil oleh init. Ini tidak sulit, tetapi Anda perlu tahu cara mengkonfigurasi inityang digunakan OS Anda. svscanbootadalah skrip yang memanggil svscan, yang melakukan pekerjaan utama mencari layanan; itu dipanggil dari initsehingga initakan mengatur untuk memulai kembali jika mati karena alasan apa pun.

Penyiapan per layanan

  1. Setiap layanan memerlukan direktori layanan , yang menyimpan informasi rumah tangga tentang layanan tersebut. Anda juga dapat membuat lokasi untuk menampung direktori layanan ini sehingga semuanya ada di satu tempat; biasanya saya gunakan /var/lib/svscan, tetapi lokasi baru mana pun akan baik-baik saja.
  2. Saya biasanya menggunakan skrip untuk menyiapkan direktori layanan, untuk menghemat banyak pekerjaan manual yang berulang. misalnya,

    sudo mkservice -d /var/lib/svscan/some-service-name -l -u user -L loguser "command line here"

    di mana some-service-namenama yang ingin Anda berikan layanan Anda, useradalah pengguna untuk menjalankan layanan itu sebagai, dan loguserpengguna untuk menjalankan logger sebagai. (Logging dijelaskan sebentar lagi.)

  3. Layanan Anda harus berjalan di latar depan . Jika program Anda berlatar belakang secara default, tetapi memiliki opsi untuk menonaktifkannya, lakukanlah. Jika program Anda berlatar belakang tanpa cara untuk menonaktifkannya, teruskan membaca fghack, meskipun ini ada untungnya : Anda tidak dapat lagi mengontrol program menggunakan svc.
  4. Edit runskrip untuk memastikannya melakukan apa yang Anda inginkan. Anda mungkin perlu sleepmenelepon di atas, jika Anda berharap layanan Anda sering keluar.
  5. Ketika semuanya sudah diatur dengan benar, buat symlink yang /servicemengarah ke direktori layanan Anda. (Jangan letakkan direktori layanan langsung di /servicedalamnya; akan lebih sulit untuk menghapus layanan dari svscanjam tangan.)

Logging

  1. Cara daemontools untuk membuat log adalah meminta layanan menulis pesan log ke keluaran standar (atau kesalahan standar, jika Anda menggunakan skrip yang dibuat dengan mkservice); svscanmenangani pengiriman pesan log ke layanan logging.
  2. Layanan logging mengambil pesan log dari input standar. Skrip layanan logging yang dibuat oleh mkserviceakan membuat file log yang dirotasi otomatis dan diberi stempel waktu di log/maindirektori. File log saat ini disebut current.
  3. Layanan logging dapat dimulai dan dihentikan secara independen dari layanan utama.
  4. Menyalurkan file log tai64nlocalakan menerjemahkan cap waktu ke dalam format yang dapat dibaca manusia. (TAI64N adalah stempel waktu atom 64-bit dengan hitungan nanodetik.)

Mengontrol layanan

  1. Gunakan svstatuntuk mendapatkan status layanan. Perhatikan bahwa layanan logging bersifat independen, dan memiliki statusnya sendiri.
  2. Anda mengontrol layanan Anda (mulai, hentikan, mulai ulang, dll.) Menggunakan svc. Misalnya, untuk memulai ulang layanan Anda, gunakan svc -t /service/some-service-name; -tberarti "kirim SIGTERM".
  3. Sinyal lain yang tersedia termasuk -h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2), dan -k( SIGKILL).
  4. Untuk menghentikan layanan, gunakan -d. Anda juga dapat mencegah layanan memulai secara otomatis saat boot dengan membuat file bernama downdalam direktori layanan.
  5. Untuk memulai layanan, gunakan -u. Ini tidak diperlukan kecuali Anda telah menurunkannya sebelumnya (atau mengaturnya untuk tidak memulai otomatis).
  6. Untuk meminta supervisor keluar, gunakan -x; biasanya digunakan dengan -duntuk menghentikan layanan juga. Ini adalah cara biasa untuk mengizinkan layanan dihapus, tetapi Anda harus membatalkan tautan layanan dari /servicepertama, atau svscansupervisor akan dimulai ulang. Selain itu, jika Anda membuat layanan dengan layanan logging ( mkservice -l), ingatlah untuk juga keluar dari supervisor logging (misalnya svc -dx /var/lib/svscan/some-service-name/log) sebelum menghapus direktori layanan.

Ringkasan

Kelebihan:

  1. daemontools menyediakan cara antipeluru untuk membuat dan mengelola layanan. Saya menggunakannya untuk server saya, dan saya sangat merekomendasikannya.
  2. Sistem pencatatannya sangat kuat, seperti halnya fasilitas layanan restart otomatis.
  3. Karena ia memulai layanan dengan skrip shell yang Anda tulis / setel, Anda dapat menyesuaikan layanan sesuka Anda.
  4. Alat kontrol layanan yang andal: Anda dapat mengirim sebagian besar sinyal apa pun ke layanan, dan dapat menaikkan dan menurunkan layanan dengan andal.
  5. Layanan Anda dijamin memiliki lingkungan eksekusi yang bersih: layanan akan dijalankan dengan lingkungan yang sama, batas proses, dll., Seperti yang initdisediakan.

Kekurangan:

  1. Setiap layanan membutuhkan sedikit penyiapan. Untungnya, ini hanya perlu dilakukan sekali per layanan.
  2. Layanan harus disiapkan untuk berjalan di latar depan. Selain itu, untuk hasil terbaik, mereka harus diatur untuk log ke output standar / kesalahan standar, daripada syslog atau file lainnya.
  3. Kurva pembelajaran yang curam jika Anda baru mengenal cara daemontools dalam melakukan sesuatu. Anda harus memulai ulang layanan menggunakan svc, dan tidak dapat menjalankan skrip jalankan secara langsung (karena mereka tidak akan berada di bawah kendali supervisor).
  4. Banyak file housekeeping, dan banyak proses housekeeping. Setiap layanan memerlukan direktori layanannya sendiri, dan setiap layanan menggunakan satu proses supervisor untuk memulai ulang layanan secara otomatis jika mati. (Jika Anda memiliki banyak layanan, Anda akan melihat banyak dari superviseproses dalam tabel proses Anda.)

Secara seimbang, menurut saya daemontools adalah sistem yang sangat baik untuk kebutuhan Anda. Saya menyambut semua pertanyaan tentang cara menyiapkan dan memeliharanya.

Chris Jester-Young
sumber
Bagaimana jawaban saya memenuhi spesifikasi: 1. Anda harus menyiapkan layanan, selama Anda tidak menyiapkan duplikat (dan selama layanan Anda tidak berlatar belakang sendiri), tidak ada duplikat yang akan terjadi. 2. supervise, pengawas, mengurus memulai kembali layanan apa pun yang keluar. Itu menunggu satu detik di antara restar; jika itu tidak cukup waktu untuk Anda, tidur di bagian atas skrip layanan dijalankan.
Chris Jester-Young
2a. supervisedidukung oleh svscan, jadi jika supervisor meninggal, itu akan dimulai kembali. 2b. svscandidukung oleh init, yang akan memulai ulang secara otomatis svscanjika diperlukan. 2c. Jika Anda initmati karena alasan apa pun, Anda tetap kacau. :-P
Chris Jester-Young
Untuk menjawab pertanyaan lain tentang housekeeping, sistem daemontools tidak menggunakan file PID, karena file tersebut bisa menjadi basi. Sebaliknya, semua informasi proses disimpan oleh supervisor yang mendukung layanan yang diberikan. Supervisor menyimpan banyak file (dan FIFO) di direktori layanan yang disukai svstatdan svcdapat dikerjakan oleh alat.
Chris Jester-Young
3
Kita harus memiliki lebih banyak posting seperti ini di SO dan di internet secara umum. Bukan hanya resep tentang cara mencapai efek yang diinginkan tetapi juga yang membutuhkan kesulitan untuk menjelaskan resepnya. Mengapa saya tidak dapat memberi suara positif ini lebih dari sekali? : |
skytreader
12

Saya pikir Anda mungkin ingin mencoba start-stop-daemon(8). Lihat skrip di /etc/init.ddistro Linux mana pun sebagai contoh. Itu dapat menemukan proses yang dimulai dengan perintah baris perintah atau file PID, sehingga cocok dengan semua kebutuhan Anda kecuali menjadi pengawas untuk skrip Anda. Tetapi Anda selalu dapat memulai skrip pengawas daemon lain yang hanya memulai ulang skrip Anda jika perlu.

Alex B
sumber
Belum ada daemon start-stop di Fedora, jadi skrip yang bergantung padanya tidak portabel. Lihat: fedoraproject.org/wiki/Features/start-stop-daemon
Bengt
Hanya pemberitahuan untuk pengguna OSX: tidak start-stop-daemonada, baik (mulai 10.9).
mklement0
@ mklement0 Yah ... banyak yang berubah dalam hampir 5 tahun.
Alex B
Ya ampun, bagaimana waktu berlalu. start-stop-daemonmasih hidup dan aktif di Linux; tapi setelah membaca jawaban stackoverflow.com/a/525406/45375 saya menyadari bahwa OSX melakukan hal sendiri: launchd.
mklement0
12

Anda harus melihat daemonize . Ini memungkinkan untuk mendeteksi salinan kedua (tetapi menggunakan mekanisme penguncian file). Juga bekerja pada distribusi UNIX dan Linux yang berbeda.

Jika Anda perlu menjalankan aplikasi Anda sebagai daemon secara otomatis, Anda perlu membuat skrip init yang sesuai.

Anda dapat menggunakan template berikut:

#!/bin/sh
#
# mydaemon     This shell script takes care of starting and stopping
#               the <mydaemon>
#

# Source function library
. /etc/rc.d/init.d/functions


# Do preliminary checks here, if any
#### START of preliminary checks #########


##### END of preliminary checks #######


# Handle manual control parameters like start, stop, status, restart, etc.

case "$1" in
  start)
    # Start daemons.

    echo -n $"Starting <mydaemon> daemon: "
    echo
    daemon <mydaemon>
    echo
    ;;

  stop)
    # Stop daemons.
    echo -n $"Shutting down <mydaemon>: "
    killproc <mydaemon>
    echo

    # Do clean-up works here like removing pid files from /var/run, etc.
    ;;
  status)
    status <mydaemon>

    ;;
  restart)
    $0 stop
    $0 start
    ;;

  *)
    echo $"Usage: $0 {start|stop|status|restart}"
    exit 1
esac

exit 0
uthark
sumber
1
Sepertinya kandidat untuk jawaban yang benar. Terutama mengingat "pemeriksaan contoh tunggal" nya.
Martin Wickman
Ini mungkin jawaban terbaik - saya tidak yakin - tetapi jika Anda berpikir demikian, dapatkah Anda juga menyertakan penjelasan mengapa spesifikasi yang saya berikan dalam pertanyaan itu salah arah?
Dreeves
Saya tidak suka bagian killprocyang berhenti: jika Anda memiliki proses yang, katakanlah, berjalan java, itu killprocakan menyebabkan semua proses Java lainnya dimatikan juga.
Chris Jester-Young
1
Dari /etc/rc.d/init.d/functions, daemonize baru saja memulai biner dari shell baru: $ cgroup $ nice / bin / bash -c $corelimit >/dev/null 2>&1 ; $*Jadi saya ragu ini akan membuat daemonisasi apa pun ...
mbonnin
1
Saya tahu ini sudah tua, tetapi bagi siapa pun yang menemukannya nanti ... ini benar. "daemon" sebagaimana didefinisikan dalam /etc/init.d/functions sebenarnya tidak melakukan daemonisasi untuk Anda. Itu hanya wrapper untuk melakukan cgroups, cek jika proses sudah berjalan, mengatur pengguna, set bagus dan ulimit nilai-nilai, dll Ini tidak daemonize proses untuk Anda. Itu masih pekerjaanmu sendiri. :)
jakem
7

Sebagai alternatif dari yang telah disebutkan daemonizedan daemontools, ada perintah daemon dari paket libslack.

daemon cukup dapat dikonfigurasi dan tidak peduli dengan semua hal daemon yang membosankan seperti restart otomatis, logging atau penanganan pidfile.

jefz
sumber
5

Jika Anda menggunakan OS X secara khusus, saya sarankan Anda melihat cara kerja launchd. Ini akan secara otomatis memeriksa untuk memastikan skrip Anda berjalan dan meluncurkannya kembali jika perlu. Ini juga mencakup semua jenis fitur penjadwalan, dll. Ini harus memenuhi persyaratan 1 dan 2.

Untuk memastikan hanya satu salinan skrip Anda yang dapat berjalan, Anda perlu menggunakan file PID. Umumnya saya menulis file ke /var/run/.pid yang berisi PID dari instance yang sedang berjalan. jika file tersebut ada saat program dijalankan, ia memeriksa apakah PID dalam file tersebut benar-benar berjalan (program mungkin macet atau lupa untuk menghapus file PID). Jika ya, batalkan. Jika tidak, mulai jalankan dan timpa file PID.

Kamil Kisiel
sumber
5

Daemontools ( http://cr.yp.to/daemontools.html ) adalah sekumpulan utilitas hard-core yang cukup digunakan untuk melakukan ini, ditulis oleh dj bernstein. Saya telah menggunakan ini dengan beberapa keberhasilan. Bagian yang mengganggu tentang itu adalah bahwa tidak ada skrip yang mengembalikan hasil yang terlihat saat Anda menjalankannya - hanya kode pengembalian yang tidak terlihat. Tapi begitu itu berjalan itu antipeluru.

multiplikasi cepat
sumber
Ya, saya akan menulis entri yang menggunakan daemontools juga. Saya akan menulis postingan saya sendiri, karena saya berharap jawaban saya jauh lebih lengkap, dan berharap mendapatkan bounty dengan cara itu. Kita lihat saja nanti. :-)
Chris Jester-Young
3

Dapatkan pertama createDaemon()dari http://code.activestate.com/recipes/278731/

Kemudian kode utamanya:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)
Douglas Leeder
sumber
Ooh, terima kasih! Ingin membuatnya sedikit lebih umum sehingga Anda dapat melakukan "daemonize foo arg1 arg2" serta "daemonize 'foo arg1 arg2'"?
Dreeves
Oke, sekarang argumen akan bergabung - namun Anda harus mengubahnya jika Anda ingin memiliki spasi di dalam argumen Anda.
Douglas Leeder
Terima kasih Douglas! Namun ada kelemahan besar: menjalankan "daemonize foo" dua kali akan memulai dua salinan dari foo running.
Dreeves
Anda dapat menambahkan beberapa kode perekaman PID, tetapi mungkin yang terbaik adalah hanya menjalankan skrip sekali ...
Douglas Leeder
Saya menganggap itu sebagai dasar dari seluruh konsep pembungkus "daemonisasi". (Misalnya, saya kemudian dapat merekamnya setiap jam atau menit untuk memastikannya selalu berjalan.) Apakah saya memikirkan hal ini salah? Apakah createDaemon sudah menjamin itu? Bagaimana setelah reboot?
Dreeves
1

Ini adalah versi yang berfungsi lengkap dengan contoh yang dapat Anda salin ke direktori kosong dan coba (setelah menginstal dependensi CPAN, yaitu Getopt :: Long , File :: Spec , File :: Pid , dan IPC :: System: : Sederhana - semuanya sangat standar dan sangat disarankan untuk peretas mana pun: Anda dapat menginstal semuanya sekaligus dengan cpan <modulename> <modulename> ...).


keepAlive.pl:

#!/usr/bin/perl

# Usage:
# 1. put this in your crontab, to run every minute:
#     keepAlive.pl --pidfile=<pidfile> --command=<executable> <arguments>
# 2. put this code somewhere near the beginning of your script,
#    where $pidfile is the same value as used in the cron job above:
#     use File::Pid;
#     File::Pid->new({file => $pidfile})->write;

# if you want to stop your program from restarting, you must first disable the
# cron job, then manually stop your script. There is no need to clean up the
# pidfile; it will be cleaned up automatically when you next call
# keepAlive.pl.

use strict;
use warnings;

use Getopt::Long;
use File::Spec;
use File::Pid;
use IPC::System::Simple qw(system);

my ($pid_file, $command);
GetOptions("pidfile=s"   => \$pid_file,
           "command=s"   => \$command)
    or print "Usage: $0 --pidfile=<pidfile> --command=<executable> <arguments>\n", exit;

my @arguments = @ARGV;

# check if process is still running
my $pid_obj = File::Pid->new({file => $pid_file});

if ($pid_obj->running())
{
    # process is still running; nothing to do!
    exit 0;
}

# no? restart it
print "Pid " . $pid_obj->pid . " no longer running; restarting $command @arguments\n";

system($command, @arguments);

example.pl:

#!/usr/bin/perl

use strict;
use warnings;

use File::Pid;
File::Pid->new({file => "pidfile"})->write;

print "$0 got arguments: @ARGV\n";

Sekarang Anda dapat menjalankan contoh di atas dengan: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3dan file pidfileakan dibuat, dan Anda akan melihat hasilnya:

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3
Eter
sumber
Saya yakin ini tidak cukup untuk spesifikasi, jika saya mengerti dengan benar. Dalam solusi Anda (terima kasih, btw!) Program yang ingin Anda daemonisasi harus dimodifikasi untuk menulis PID-nya ke file PID. Saya berharap ada utilitas yang dapat mengubah skrip arbitrer.
Dreeves
@dreeves: ya, tetapi ada dua cara untuk melakukannya: 1. skrip yang dipanggil oleh keepAlive.pl (misalnya example.pl) bisa menjadi pembungkus untuk menjalankan program sebenarnya, atau 2. keepAlive.pl bisa mengurai tabel proses sistem aktif (dengan Proc :: ProcessTable CPAN) untuk mencoba menemukan proses yang relevan dan pidnya).
Eter
1

Anda juga dapat mencoba Monit . Monit adalah layanan yang memantau dan melaporkan layanan lain. Meskipun ini terutama digunakan sebagai cara untuk memberi tahu (melalui email dan sms) tentang masalah runtime, ini juga dapat melakukan apa yang disarankan oleh sebagian besar saran lain di sini. Itu dapat secara otomatis memulai dan menghentikan program, mengirim email, memulai skrip lain, dan memelihara log output yang dapat Anda ambil. Selain itu, saya merasa mudah untuk menginstal dan memelihara karena ada dokumentasi yang solid.

Adestin
sumber
1

Anda dapat mencoba untuk abadi Ini adalah pengawas lintas platform * nix (OS agnostik).

Untuk mencoba cepat di macOS:

brew install immortal

Jika Anda menggunakan FreeBSD dari port atau dengan menggunakan pkg:

pkg install immortal

Untuk Linux dengan mengunduh binari yang telah dikompilasi atau dari sumber: https://immortal.run/source/

Anda bisa menggunakannya seperti ini:

immortal -l /var/log/date.log date

Atau dengan file YAML konfigurasi yang memberi Anda lebih banyak opsi, misalnya:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log

Jika Anda juga ingin menyimpan output kesalahan standar dalam file terpisah, Anda dapat menggunakan sesuatu seperti:

cmd: date
log:
    file: /var/log/date.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
stderr:
    file: /var/log/date-error.log
    age: 86400 # seconds
    num: 7     # int
    size: 1    # MegaBytes
    timestamp: true # will add timesamp to log
nbari
sumber
0

Saya telah melakukan serangkaian perbaikan pada jawaban lainnya .

  1. stdout dari skrip ini murni terdiri dari stdout yang berasal dari anaknya KECUALI itu keluar karena mendeteksi bahwa perintah sudah dijalankan
  2. membersihkan setelah pidfile-nya saat dihentikan
  3. periode waktu tunggu yang dapat dikonfigurasi opsional (Menerima argumen numerik positif apa pun, mengirim ke sleep)
  4. permintaan penggunaan aktif -h
  5. eksekusi perintah sewenang-wenang, bukan eksekusi perintah tunggal. Arg terakhir ATAU argumen yang tersisa (jika lebih dari satu argumen terakhir) dikirim ke eval, sehingga Anda dapat membuat skrip shell apa pun sebagai string untuk dikirim ke skrip ini sebagai argumen terakhir (atau argumen tambahan) agar dapat melakukan daemonisasi
  6. perbandingan jumlah argumen dilakukan dengan -ltalih - alih<

Berikut skripnya:

#!/bin/sh

# this script builds a mini-daemon, which isn't a real daemon because it
# should die when the owning terminal dies, but what makes it useful is
# that it will restart the command given to it when it completes, with a
# configurable timeout period elapsing before doing so.

if [ "$1" = '-h' ]; then
    echo "timeout defaults to 1 sec.\nUsage: $(basename "$0") sentinel-pidfile [timeout] command [command arg [more command args...]]"
    exit
fi

if [ $# -lt 2 ]; then
    echo "No command given."
    exit
fi

PIDFILE=$1
shift

TIMEOUT=1
if [[ $1 =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
        TIMEOUT=$1
        [ $# -lt 2 ] && echo "No command given (timeout was given)." && exit
        shift
fi

echo "Checking pid in file ${PIDFILE}." >&2

#Check to see if process running.
if [ -f "$PIDFILE" ]; then
    PID=$(< $PIDFILE)
    if [ $? = 0 ]; then
        ps -p $PID >/dev/null 2>&1
        if [ $? = 0 ]; then
            echo "This script is (probably) already running as PID ${PID}."
            exit
        fi
    fi
fi

# Write our pid to file.
echo $$ >$PIDFILE

cleanup() {
        rm $PIDFILE
}
trap cleanup EXIT

# Run command until we're killed.
while true; do
    eval "$@"
    echo "I am $$ and my child has exited; restart in ${TIMEOUT}s" >&2
    sleep $TIMEOUT
done

Pemakaian:

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/'
Checking pid in file pidfilefortesting.
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
azzzcd
I am 79281 and my child has exited; restart in 0.5s
^C

$ term-daemonize.sh pidfilefortesting 0.5 'echo abcd | sed s/b/zzz/' 2>/dev/null
azzzcd
azzzcd
azzzcd
^C

Berhati-hatilah jika Anda menjalankan skrip ini dari direktori yang berbeda, skrip ini mungkin menggunakan pidfile yang berbeda dan tidak mendeteksi instance yang sedang berjalan. Karena ini dirancang untuk menjalankan dan memulai kembali perintah sementara yang disediakan melalui argumen, tidak ada cara untuk mengetahui apakah sesuatu telah dimulai, karena siapa yang akan mengatakan apakah itu perintah yang sama atau tidak? Untuk meningkatkan penegakan ini yang hanya menjalankan satu contoh dari sesuatu, diperlukan solusi khusus untuk situasi tersebut.

Selain itu, agar berfungsi sebagai daemon yang tepat, Anda harus menggunakan (minimal) nohup sebagai jawaban lain yang menyebutkan. Saya tidak berusaha memberikan ketahanan apa pun terhadap sinyal yang mungkin diterima proses.

Satu hal lagi yang perlu diperhatikan adalah bahwa membunuh skrip ini (jika dipanggil dari skrip lain yang dimatikan, atau dengan sinyal) mungkin tidak berhasil membunuh anak tersebut, terutama jika anak itu adalah skrip lain . Saya tidak yakin mengapa ini terjadi, tetapi tampaknya ada sesuatu yang berhubungan dengan cara evalkerja, yang misterius bagi saya. Jadi mungkin bijaksana untuk mengganti baris itu dengan sesuatu yang hanya menerima satu perintah seperti di jawaban lain.

Steven Lu
sumber