আমি কীভাবে ইউনিক্সে একটি স্বেচ্ছাসেবক স্ক্রিপ্ট ডেমনাইজ করতে পারি?


94

আমি একটি daemonizer করে একটি মধ্যে একটি অবাধ জেনেরিক স্ক্রিপ্ট অথবা কমান্ড চালু করতে পারেন চাই ডেমন

দুটি সাধারণ ক্ষেত্রে আমি মোকাবেলা করতে চাই:

  1. আমার একটি স্ক্রিপ্ট রয়েছে যা চিরকালের জন্য চালানো উচিত। যদি এটি কখনও মারা যায় (বা পুনরায় চালু হয়) তবে এটি পুনরায় চালু করুন। একবারে দু'টি অনুলিপি চলতে দেবেন না (কোনও অনুলিপি ইতিমধ্যে চলছে কিনা তা সনাক্ত করুন এবং সে ক্ষেত্রে এটি আরম্ভ করবেন না)।

  2. আমার কাছে একটি সাধারণ স্ক্রিপ্ট বা কমান্ড লাইন কমান্ড রয়েছে যা আমি বারবার চালিয়ে যেতে চাই চিরকাল (রানের মধ্যে একটি ছোট বিরতি দিয়ে)। আবার, স্ক্রিপ্টের দুটি অনুলিপি একবারে একবারে চলতে দেবেন না।

অবশ্যই 2 ক্ষেত্রে স্ক্রিপ্টের আশেপাশে "যখন (সত্য)" লুপ লিখতে হবে এবং তারপরে কেস 1 এর জন্য একটি সমাধান প্রয়োগ করা হবে তবে আরও সাধারণ সমাধান কেবল ক্ষেত্রে 2 কে সমাধান করবে যেহেতু এটি স্ক্রিপ্ট 1 এর ক্ষেত্রে প্রযোজ্য as ভাল (আপনি শুধু একটি সংক্ষিপ্ত অথবা কোন বিরতি করতে পারেন যদি স্ক্রিপ্ট কি কখনো (অবশ্যই মরতে উদ্দেশ্যে না হয় যদি স্ক্রিপ্ট সত্যিই নেই তারপর মারা যায় না বিরতি নেই আসলে ব্যাপার))।

দ্রষ্টব্য যে সমাধানটি অন্তর্ভুক্ত করা উচিত নয়, বলুন, বিদ্যমান স্ক্রিপ্টগুলিতে ফাইল-লকিং কোড বা পিআইডি রেকর্ডিং যুক্ত করুন।

আরও সুনির্দিষ্টভাবে, আমি একটি প্রোগ্রাম "ডিমনাইজ" করতে চাই যা আমি পছন্দ করতে পারি

% daemonize myscript arg1 arg2

বা, উদাহরণস্বরূপ,

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

যা টাইমস.টি.এস.টি. তে সংযুক্ত তারিখগুলির ক্রমবর্ধমান তালিকা রাখবে। (মনে রাখবেন যে ডিমনাইজ করার আর্গুমেন্টগুলি যদি স্ক্রিপ্ট হয় যা উপরের 1 এর মতো চিরকালের জন্য চালিত হয়, তবে ডেমোনাইজটি সঠিক কাজটি করবে, যখন প্রয়োজন হবে তখন এটি পুনরায় আরম্ভ করবে)) আমি তখন আমার লগিনে উপরের মতো একটি আদেশ দিতে পারি could এবং / বা এটি ক্রোন ঘন্টা বা মিনিটে (অপ্রত্যাশিতভাবে মারা যাবার বিষয়ে আমি কতটা উদ্বিগ্ন ছিল তার উপর নির্ভরশীল)।

এনবি: ডেমোনাইজ স্ক্রিপ্টটি কমান্ড স্ট্রিংটি ডেমোনাইজ করার কথা মনে রাখতে হবে যাতে একই কমান্ডের স্ট্রিংটি আবার ডিমনাইজড করা হয় তবে এটি দ্বিতীয় অনুলিপি চালু করে না।

এছাড়াও, সমাধানটি ওএস এক্স এবং লিনাক্স উভয় ক্ষেত্রেই আদর্শভাবে কাজ করা উচিত তবে এক বা অন্যের জন্য সমাধানগুলি স্বাগত।

সম্পাদনা: আপনি যদি এটির সাথে যোগাযোগ করতে চান তবে এটি ঠিক আছে sudo daemonize myscript myargs

(আমি যদি এই সমস্ত ভুলের কথা ভাবছি বা দ্রুত এবং নোংরা আংশিক সমাধান রয়েছে, আমি এটি শুনতেও পছন্দ করব))


পিএস: যদি এটি দরকারী হয় তবে এখানে অজগর সম্পর্কিত একটি অনুরূপ প্রশ্ন রয়েছে।

এবং অনুরূপ প্রশ্নের এই উত্তরটিতে একটি স্বেচ্ছাসেবীর লিপির দ্রুত এবং মলিন রাক্ষসীকরণের জন্য দরকারী উত্সাহ বলে মনে হয়:


4
খাঁটি শেল সংস্করণের জন্য সার্ভারফল্ট / প্রশ্নগুলি / ৩১১৯11৩/২ দেখুন
w00t

উত্তর:


93

আপনি নোহুপ এবং অ্যান্ড অপারেটর ব্যবহার করে ইউনিক্সের যে কোনও এক্সিকিউটেবল ডিমনাইজাইজ করতে পারেন:

nohup yourScript.sh script args&

নোহপ কমান্ড আপনাকে আপনার স্ক্রিপ্টটি না মেরে আপনার শেল সেশনটি বন্ধ করার অনুমতি দেয়, যখন & আপনার স্ক্রিপ্টটিকে পটভূমিতে রাখে যাতে আপনি সেশন চালিয়ে যাওয়ার জন্য শেল প্রম্পট পান। এটির সাথে একমাত্র ছোটখাটো সমস্যা হ'ল স্ট্যান্ডার্ড আউট এবং স্ট্যান্ডার্ড ত্রুটি উভয়ই ./nohup.out এ প্রেরণ করা হয়, সুতরাং আপনি যদি এই ম্যানোয়ারটিতে বেশ কয়েকটি স্ক্রিপ্ট শুরু করেন তবে তাদের আউটপুট গতিবদ্ধ হবে। আরও ভাল আদেশ হবে:

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

এটি আপনার পছন্দের ফাইলটিতে স্ট্যান্ডার্ড আউট এবং আপনার পছন্দের একটি পৃথক ফাইলে মান ত্রুটি প্রেরণ করবে। আপনি যদি স্ট্যান্ডার্ড আউট এবং স্ট্যান্ডার্ড ত্রুটি উভয়ের জন্য কেবল একটি ফাইল ব্যবহার করতে চান তবে আপনি আমাদের এটি করতে পারেন:

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

2> & 1 শেলটিকে স্ট্যান্ডার্ড আউট (ফাইল বর্ণনাকারী 1) হিসাবে একই ফাইলটিতে স্ট্যান্ডার্ড ত্রুটি (ফাইল বর্ণনাকারী 2) পুনর্নির্দেশ করতে বলে।

কেবল একবার কমান্ড চালাতে এবং এটি মারা গেলে পুনরায় চালু করতে আপনি এই স্ক্রিপ্টটি ব্যবহার করতে পারেন:

#!/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

প্রথম যুক্তিটি হ'ল পিড ফাইলটির নাম। দ্বিতীয় যুক্তি হ'ল কমান্ড। এবং অন্যান্য সমস্ত যুক্তি হ'ল কমান্ডের যুক্তি।

আপনি যদি এই স্ক্রিপ্টটির নাম পুনরায় চালু করেন sh

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

অসাধারণ; ধন্যবাদ. আমি ভাবছি যদি এটি পুনরায় আরম্ভ করতে বিলম্বের জন্য একটি বিকল্প থাকতে পারে। অথবা হয়ত ভাল শুধু এই সাথে এটি ব্যবহার করার জন্য: stackoverflow.com/questions/555116/...
dreeves

4
এটি কেবল সাইনইচআপ পরিচালনা করে, অন্যান্য (সাধারণত) মারাত্মক সংকেত রয়েছে যা পরিচালনা করা উচিত।
টিম পোস্ট

এই স্ক্রিপ্টটি উন্নত করার আরেকটি উপায় সম্ভবত এটি একটি আর্গ হিসাবে নির্দিষ্ট করার জন্য এটির পরিবর্তে $ পিআইডিএফআইএলকে নিজের জায়গায় স্থাপন করার জন্য একটি ভাল অবস্থান নিয়ে আসা উচিত। এমনকি এটি নিজের পরে পরিষ্কার হয় না! (যা একটি দিয়ে সোজা হওয়া উচিত trap EXIT)
স্টিভেন লু

এছাড়াও, যে বিবেচনা ব্যবহার <মধ্যে testএকটি ASCII তুলনা না একটি পূর্ণসংখ্যা তুলনা হয়। এটি এখনও কাজ করতে পারে তবে বাগগুলি হতে পারে।
স্টিভেন লু

আমি এই স্ক্রিপ্টে আমার ফিক্সগুলি এখানে পোস্ট করেছি ।
স্টিভেন লু

34

আমি দীর্ঘ উত্তরের জন্য ক্ষমাপ্রার্থী (দয়া করে আমার উত্তরটি কী করে তা সম্পর্কে মন্তব্যগুলি দেখুন)। আমি বিস্তৃত হওয়ার চেষ্টা করছি, সুতরাং আপনার পক্ষে যতটা সম্ভব ভাল লেগেছে। :-)

আপনি যদি প্রোগ্রামগুলি ইনস্টল করতে সক্ষম হন (মূল অ্যাক্সেস থাকতে পারে) এবং ডেমন সম্পাদনের জন্য আপনার স্ক্রিপ্ট সেট আপ করার জন্য ওয়ান-টাইম লেগওয়ার্ক করতে ইচ্ছুক হন (অর্থাত্ কমান্ড লাইনে চালানোর জন্য কমান্ড-লাইন আর্গুমেন্টগুলি নির্দিষ্ট করার চেয়ে আরও বেশি জড়িত, তবে কেবলমাত্র প্রতি পরিষেবাতে একবার করা দরকার), আমার কাছে এমন উপায় রয়েছে যা আরও দৃust়।

এটি ডিমনটোলগুলি ব্যবহার করে । পোস্টের বাকী অংশটি কীভাবে ডেমনটোলগুলি ব্যবহার করে পরিষেবাদি সেট আপ করবেন তা বর্ণনা করে।

প্রাথমিক সেটআপ

  1. ডিমনটোলগুলি কীভাবে ইনস্টল করবেন সে সম্পর্কে নির্দেশাবলী অনুসরণ করুন । কিছু বিতরণ (যেমন, দেবিয়ান, উবুন্টু) এর জন্য ইতিমধ্যে প্যাকেজ রয়েছে, সুতরাং এটি কেবল ব্যবহার করুন।
  2. বলা একটি ডিরেক্টরি করুন /service। ইনস্টলারটির এটি ইতিমধ্যে করা উচিত ছিল, তবে কেবল যাচাই করুন বা ম্যানুয়ালি ইনস্টল করা উচিত। আপনি যদি এই অবস্থানটি অপছন্দ করেন তবে আপনি এটি আপনার svscanbootস্ক্রিপ্টে পরিবর্তন করতে পারবেন , যদিও বেশিরভাগ ডিমনটোল ব্যবহারকারীরা ব্যবহার করতে অভ্যস্ত /serviceএবং যদি আপনি এটি ব্যবহার না করেন তবে বিভ্রান্ত হয়ে পড়বেন ।
  3. আপনি যদি উবুন্টু বা অন্য কোনও ডিস্ট্রো ব্যবহার করেন যা মান ব্যবহার করে না init(যেমন, ব্যবহার করে না /etc/inittab), আপনাকে inittabডাকার ব্যবস্থা svscanbootকরার জন্য আপনাকে পূর্ব- ইনস্টলটি বেস হিসাবে ব্যবহার করতে হবে init। এটি শক্ত নয়, তবে initআপনার ওএস যে ব্যবহার করে সেটি কনফিগার করতে হবে তা আপনার জানতে হবে। svscanbootকল এমন একটি স্ক্রিপ্ট svscanযা পরিষেবাগুলির সন্ধানের মূল কাজটি করে; এটি initতাই বলা হয়েছে initএটি কোনও কারণে মারা গেলে এটি পুনরায় চালু করার ব্যবস্থা করবে।

প্রতি পরিষেবা সেটআপ

  1. প্রতিটি পরিষেবায় একটি পরিষেবা ডিরেক্টরি প্রয়োজন , যা পরিষেবা সম্পর্কে গৃহস্থালি সম্পর্কিত তথ্য সঞ্চয় করে। এই পরিষেবা ডিরেক্টরিগুলি রাখার জন্য আপনি একটি অবস্থানও তৈরি করতে পারেন যাতে সেগুলি সমস্ত এক জায়গায় থাকে; সাধারণত আমি ব্যবহার করি /var/lib/svscan, তবে যে কোনও নতুন অবস্থান ঠিক থাকবে।
  2. আমি প্রচুর ম্যানুয়াল পুনরাবৃত্তিমূলক কাজগুলি সংরক্ষণ করার জন্য পরিষেবা ডিরেক্টরি সেটআপ করতে সাধারণত একটি স্ক্রিপ্ট ব্যবহার করি । যেমন,

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

    যেখানে some-service-nameআপনি যে নামটি আপনার সেবা দিতে চান হয়, userযেমন যে সেবা চালানোর জন্য ব্যবহারকারী, এবং loguserযেমন এটির চালানোর জন্য ব্যবহারকারী। (লগিংয়ের বিষয়টি সামান্য কিছুটা ব্যাখ্যা করা হয়েছে।

  3. আপনার পরিষেবাটি অগ্রভাগে চলতে হবে । যদি ডিফল্টরূপে আপনার প্রোগ্রামের ব্যাকগ্রাউন্ড থাকে তবে তা অক্ষম করার বিকল্প থাকে তবে তা করুন। যদি আপনার প্রোগ্রামের ব্যাকগ্রাউন্ডগুলি এটি নিষ্ক্রিয় করার উপায় না করে থাকে তবে তা পড়ুন fghack, যদিও এটি বাণিজ্য-বন্ধে আসে: আপনি আর ব্যবহার করে প্রোগ্রামটি নিয়ন্ত্রণ করতে পারবেন না svc
  4. runআপনি যা চান তা এটি সম্পাদন করার জন্য স্ক্রিপ্টটি সম্পাদনা করুন । আপনি sleepযদি আপনার পরিষেবাটি ঘন ঘন প্রস্থান করার প্রত্যাশা করেন তবে আপনাকে শীর্ষে একটি কল করার প্রয়োজন হতে পারে ।
  5. যখন সবকিছু ঠিকঠাক করা হয় তখন /serviceআপনার পরিষেবা ডিরেক্টরিকে নির্দেশ করে একটি সিমিলিংক তৈরি করুন । (সার্ভিস ডিরেক্টরিগুলি সরাসরি এর মধ্যে রাখবেন না /service; পরিষেবাটি svscanনজরদারি থেকে সরানো আরও কঠিন করে তোলে ))

লগিং

  1. লগিংয়ের ডিমনটোলস উপায় হ'ল পরিষেবাটি স্ট্যান্ডার্ড আউটপুটটিতে লগ বার্তা লেখার জন্য (বা আপনি যদি তৈরি স্ক্রিপ্টগুলি ব্যবহার করেন তবে স্ট্যান্ডার্ড ত্রুটি mkservice); svscanলগিং পরিষেবাতে লগ বার্তা প্রেরণের যত্ন নেয়।
  2. লগিং পরিষেবা স্ট্যান্ডার্ড ইনপুট থেকে লগ বার্তা নেয়। দ্বারা নির্মিত লগিং পরিষেবা স্ক্রিপ্ট ডিরেক্টরিটিতে mkserviceস্বয়ংক্রিয়-ঘোরানো, টাইমস্ট্যাম্পড লগ ফাইলগুলি তৈরি করবে log/main। বর্তমান লগ ফাইল বলা হয় current
  3. লগিং পরিষেবাটি মূল পরিষেবাটি থেকে স্বাধীনভাবে শুরু করা এবং বন্ধ করা যেতে পারে।
  4. এর মাধ্যমে লগ ফাইলগুলিকে পাইপ দেওয়া tai64nlocalটাইমস্ট্যাম্পগুলিকে মানব-পঠনযোগ্য ফর্ম্যাটে অনুবাদ করবে। (TAI64N একটি ন্যানোসেকেন্ড গণনা সহ একটি 64-বিট পারমাণবিক টাইমস্ট্যাম্প)

নিয়ন্ত্রণ সেবা

  1. svstatকোনও পরিষেবার স্থিতি পেতে ব্যবহার করুন । নোট করুন যে লগিং পরিষেবাটি স্বতন্ত্র এবং এর নিজস্ব অবস্থান রয়েছে।
  2. আপনি আপনার পরিষেবা (স্টার্ট, স্টপ, পুনঃসূচনা ইত্যাদি) ব্যবহার করে নিয়ন্ত্রণ করেন svc। উদাহরণস্বরূপ, আপনার পরিষেবাটি পুনরায় চালু করতে, ব্যবহার করুন svc -t /service/some-service-name; -tঅর্থ "প্রেরণ SIGTERM"।
  3. উপলব্ধ অন্যান্য সংকেতগুলির মধ্যে রয়েছে -h( SIGHUP), -a( SIGALRM), -1( SIGUSR1), -2( SIGUSR2), এবং -k( SIGKILL)।
  4. পরিষেবাটি নিচে নামাতে, ব্যবহার করুন -ddownপরিষেবা ডিরেক্টরিতে নামের একটি ফাইল তৈরি করে আপনি কোনও পরিষেবা স্বয়ংক্রিয়ভাবে বুটআপ থেকে শুরু করতে বাধা দিতে পারেন ।
  5. পরিষেবাটি শুরু করতে, ব্যবহার করুন -u। আপনি এটি পূর্বে অবনমিত না করা (বা এটি স্বয়ংক্রিয়-প্রারম্ভের জন্য সেট আপ না করা) প্রয়োজন হয় না।
  6. সুপারভাইজারকে প্রস্থান করতে বলার জন্য, ব্যবহার করুন -x; সাধারণত -dপরিষেবাটি বন্ধ করতে ব্যবহার করা হয়। কোনও পরিষেবা অপসারণের অনুমতি দেওয়ার এটাই স্বাভাবিক উপায়, তবে আপনাকে /serviceপ্রথমে পরিষেবাটি লিঙ্কমুক্ত করতে svscanহবে , অন্যথায় তদারককারী পুনরায় চালু করবেন। এছাড়াও, যদি আপনি লগিং পরিষেবা ( mkservice -l) দিয়ে আপনার পরিষেবা তৈরি করেন svc -dx /var/lib/svscan/some-service-name/logতবে পরিষেবা ডিরেক্টরি সরানোর আগে লগিং সুপারভাইজার (যেমন, ) থেকে বেরিয়ে আসাও মনে রাখবেন ।

সারসংক্ষেপ

পেশাদাররা:

  1. ডিমনটোলস পরিষেবাগুলি তৈরি এবং পরিচালনা করার জন্য একটি বুলেটপ্রুফ উপায় সরবরাহ করে। আমি এটি আমার সার্ভারগুলির জন্য ব্যবহার করি এবং আমি এটির সুপারিশ করি।
  2. এর লগিং সিস্টেমটি খুব শক্তিশালী, যেমন পরিষেবা অটো-রিস্টার্ট সুবিধা।
  3. যেহেতু এটি শেল স্ক্রিপ্ট দিয়ে পরিষেবাগুলি শুরু করে যা আপনি লেখেন / টিউন করেন, আপনি নিজের পছন্দ অনুযায়ী আপনার পরিষেবাটি তৈরি করতে পারেন।
  4. শক্তিশালী পরিষেবা নিয়ন্ত্রণ সরঞ্জামগুলি: আপনি কোনও পরিষেবায় সর্বাধিক কোনও সংকেত প্রেরণ করতে পারেন এবং পরিষেবাগুলি বিশ্বস্তভাবে উপরে এবং নীচে আনতে পারেন।
  5. আপনার পরিষেবাদিগুলি একটি পরিষ্কার কার্যকর পরিবেশের গ্যারান্টিযুক্ত: তারা যা সরবরাহ করে একই পরিবেশ, প্রক্রিয়া সীমা ইত্যাদি দিয়ে কার্যকর করবে init

কনস:

  1. প্রতিটি পরিষেবা কিছুটা সেটআপ নেয়। ধন্যবাদ, প্রতি পরিষেবা প্রতি একবার করার দরকার doing
  2. অগ্রভাগে চালনার জন্য পরিষেবাগুলি সেট আপ করতে হবে। এছাড়াও, সর্বোত্তম ফলাফলের জন্য, সেগুলিকে বা অন্যান্য ফাইলের পরিবর্তে স্ট্যান্ডার্ড আউটপুট / স্ট্যান্ডার্ড ত্রুটিতে লগ করতে সেট আপ করা উচিত।
  3. আপনি কাজ করার ডিমন্টোলস পদ্ধতিতে নতুন হন তবে খাড়া শেখার বক্ররেখা। আপনাকে পরিষেবাগুলি পুনরায় চালু svcকরতে হবে এবং সরাসরি রান স্ক্রিপ্টগুলি চালাতে পারবেন না (যেহেতু তারা তখন সুপারভাইজারের নিয়ন্ত্রণে থাকবে না)।
  4. প্রচুর হাউসকিপিং ফাইল এবং প্রচুর বাড়ির রক্ষণাবেক্ষণ প্রক্রিয়া। প্রতিটি পরিষেবার নিজস্ব পরিষেবা ডিরেক্টরি প্রয়োজন এবং প্রতিটি পরিষেবা যদি মারা যায় তবে পরিষেবাটি স্বয়ংক্রিয়-পুনঃসূচনা করতে একটি সুপারভাইজার প্রক্রিয়া ব্যবহার করে। (আপনি অনেক পরিষেবা থাকে, তাহলে আপনি দেখতে পাবেন প্রচুর এর superviseআপনার প্রক্রিয়া টেবিলে প্রসেস।)

ভারসাম্যহীনতায়, আমি মনে করি আপনার প্রয়োজনের জন্য ডেমনটোলগুলি একটি দুর্দান্ত সিস্টেম। এটি কীভাবে সেট আপ করা এবং এটি বজায় রাখা যায় সে সম্পর্কে আমি যে কোনও প্রশ্নের স্বাগত জানাই।


আমার উত্তরটি কীভাবে অনুমান করে: ১. আপনাকে পরিষেবাগুলি সেট আপ করতে হবে, যতক্ষণ না আপনি ডুপ্লিকেট সেট আপ করবেন না (এবং যতক্ষণ না আপনার পরিষেবা নিজেই ব্যাকগ্রাউন্ড করে না), কোনও নকল আসবে না occur ২. supervise, তত্ত্বাবধায়ক, যে কোনও পরিষেবা থেকে বেরিয়ে আসে সেগুলি পুনরায় চালু করার যত্ন নেয়। এটি পুনরুদ্ধারের মধ্যে এক সেকেন্ড অপেক্ষা করে; যদি এটি আপনার জন্য পর্যাপ্ত সময় না থাকে তবে পরিষেবা চালনার স্ক্রিপ্টের শীর্ষে একটি ঘুম দিন।
ক্রিস জেস্টার-ইয়ং

2 এ। superviseএটি নিজেই সমর্থনযুক্ত svscan, সুতরাং যদি কোনও সুপারভাইজার মারা যায় তবে এটি আবার চালু করা হবে। 2 বি। svscanদ্বারা সমর্থিত init, যা svscanপ্রয়োজন হিসাবে স্বয়ংক্রিয়ভাবে পুনঃসূচনা করবে । 2 সি। যদি initকোনও কারণে আপনার মৃত্যু হয় তবে আপনি যেভাবেই হোক না কেন ক্ষতিগ্রস্থ হন। :
ক্রিস জেস্টার-ইয়াং

গৃহকর্ম সম্পর্কে অন্যান্য প্রশ্নের উত্তর দেওয়ার জন্য, ড্যামনটোলস সিস্টেমগুলি পিআইডি ফাইলগুলি ব্যবহার করে না, কারণ তারা বাসি পেতে পারে get পরিবর্তে, সমস্ত প্রক্রিয়া সম্পর্কিত তথ্য তদারকির দ্বারা প্রদত্ত পরিষেবাটিকে সমর্থন করে রাখে। সরঞ্জাম পছন্দ সুপারভাইজার সেবা ডিরেক্টরির মধ্যে ফাইল (এবং FIFOs) একটি গুচ্ছ বজায় রাখে svstatএবং svcসঙ্গে কাজ করতে পারেন।
ক্রিস জেস্টার-ইয়ং

4
আমাদের মতো এসও এবং সাধারণভাবে আরও পোস্ট থাকতে হবে। কীভাবে পছন্দসই প্রভাব অর্জন করবেন সে সম্পর্কে একটি রেসিপি নয় তবে এমন একটি যা রেসিপিগুলি ব্যাখ্যা করতে সমস্যা নেয়। আমি কেন একাধিকবার এটি উপস্থাপন করতে পারি না? : |
স্কাই ট্র্যাডার 2

12

আমি মনে করি আপনি চেষ্টা করতে পারেন start-stop-daemon(8)/etc/init.dউদাহরণস্বরূপ কোনও লিনাক্স ডিস্ট্রোতে স্ক্রিপ্টগুলি পরীক্ষা করে দেখুন । এটি কমান্ড লাইনের মাধ্যমে অনুরোধ করা পিআইডি ফাইল বা পিআইডি ফাইলের মাধ্যমে শুরু করা প্রক্রিয়াগুলি খুঁজে পেতে পারে, সুতরাং এটি আপনার স্ক্রিপ্টের জন্য নজরদারি ছাড়া আপনার সমস্ত প্রয়োজনীয়তার সাথে মেলে। তবে আপনি সর্বদা আর একটি ডেমন ওয়াচডগ স্ক্রিপ্ট শুরু করতে পারেন যা প্রয়োজন হলে আপনার স্ক্রিপ্টটি পুনরায় চালু করে।


ফেডোরায় কোনও স্টার্ট-স্টপ-ডেমন নেই, তবে এর উপর নির্ভর করে স্ক্রিপ্টগুলি বহনযোগ্য নয়। দেখুন: fedoraproject.org/wiki/Features/start-stop-daemon
বেঙ্গট

ওএসএক্স ব্যবহারকারীদের জন্য কেবল একটি শীর্ষস্থান: start-stop-daemonসেখানে নেই, হয় (10.9 হিসাবে)।
mklement0

@ mklement0 ঠিক আছে ... প্রায় 5 বছরে অনেক কিছু পরিবর্তন হয়েছে।
অ্যালেক্স বি

আমার, কিভাবে সময় উড়ে। start-stop-daemonযদিও এখনও জীবিত এবং লিনাক্সে লাথি মারছে, যদিও; কিন্তু উত্তর পড়ার পর stackoverflow.com/a/525406/45375 আমি বুঝতে পারি যে ওএসএক্স নিজস্ব জিনিস আছে: launchd
mklement0

12

আপনার ডেমোনাইজ করা উচিত । এটি দ্বিতীয় অনুলিপি সনাক্ত করতে দেয় (তবে এটি ফাইল লকিং প্রক্রিয়া ব্যবহার করে)। এছাড়াও এটি বিভিন্ন ইউনিক্স এবং লিনাক্স বিতরণে কাজ করে।

আপনার যদি অ্যাপ্লিকেশনটি স্বয়ংক্রিয়ভাবে ডেমন হিসাবে শুরু করতে হয় তবে আপনাকে উপযুক্ত init-স্ক্রিপ্ট তৈরি করতে হবে।

আপনি নিম্নলিখিত টেমপ্লেট ব্যবহার করতে পারেন:

#!/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

4
সঠিক উত্তরের প্রার্থী বলে মনে হচ্ছে। বিশেষত এর "একক উদাহরণ পরীক্ষা করা" বিবেচনা করুন।
মার্টিন উইকম্যান

এটি সর্বোত্তম সম্ভাব্য উত্তর হতে পারে - আমি নিশ্চিত নই - তবে আপনি যদি এটি মনে করেন তবে প্রশ্নটিতে আমি যে অনুমানটি দিয়েছিলাম তা কেন মাথাচাড়া দিয়ে যায় তার একটি ব্যাখ্যা আপনিও অন্তর্ভুক্ত করতে পারেন?
dreeves

killprocথামার অংশটি আমি পছন্দ করি না : যদি আপনার এমন একটি প্রক্রিয়া থাকে যা বলে, দৌড়ায় java, তবে killprocঅন্য সমস্ত জাভা প্রক্রিয়াও হত্যার কারণ ঘটবে।
ক্রিস জেস্টার-ইয়ং

4
/Etc/rc.d/init.d/funitions থেকে, ডেমোনাইজ কেবল নতুন শেল থেকে বাইনারি শুরু করে: $ cgroup $ সুন্দর / বিন / বাশ -c $corelimit >/dev/null 2>&1 ; $*সুতরাং আমি সন্দেহ করি যে এটি কোনও কিছুকে ডিমনাইজ করতে চলেছে ...
mbonnin

4
আমি জানি এটি পুরানো, তবে যে কেউ পরে এটি খুঁজে পাবে ... এটি সঠিক। "ডেমন" /etc/init.d/function- এ সংজ্ঞায়িত হিসাবে আসলে আপনার জন্য ডিমনাইজ করে না । এটি সিগ্রুপ করার জন্য কেবল একটি মোড়ক, প্রক্রিয়াটি ইতিমধ্যে চলছে কিনা তা পরীক্ষা করে দেখুন, একটি ব্যবহারকারী সেট করুন, সুন্দর এবং উম্মিত মান নির্ধারণ করুন ইত্যাদি এটি আপনার জন্য প্রক্রিয়াটি হ্রাস করে না । এটি এখনও আপনার নিজের কাজ। :)
জাকেম

7

ইতিমধ্যে উল্লিখিত একটি বিকল্প হিসাবে daemonizeএবং daemontools, আছে ডেমন libslack প্যাকেজের কমান্ড।

daemon বেশ কনফিগারযোগ্য এবং স্বয়ংক্রিয় পুনঃসূচনা, লগিং বা পিডফিল হ্যান্ডলিংয়ের মতো সমস্ত ক্লান্তিকর ডেমন স্টাফ সম্পর্কে যত্নশীল।


5

আপনি যদি ওএস এক্স নির্দিষ্টভাবে ব্যবহার করেন তবে আমি আপনাকে পরামর্শ দিচ্ছি যে লঞ্চ কীভাবে কাজ করে তা একবার দেখুন। এটি আপনার স্ক্রিপ্টটি চলছে কিনা তা স্বয়ংক্রিয়ভাবে চেক করবে এবং প্রয়োজনে এটি পুনরায় লঞ্চ করতে হবে। এটিতে সমস্ত ধরণের সময়সূচী বৈশিষ্ট্য ইত্যাদি অন্তর্ভুক্ত রয়েছে এটির প্রয়োজন 1 এবং 2 উভয়ই পূরণ করতে হবে।

আপনার স্ক্রিপ্টের কেবল একটি অনুলিপি চলতে পারে তা নিশ্চিত করার জন্য আপনার একটি পিআইডি ফাইল ব্যবহার করা দরকার। সাধারণত আমি /var/run/.pid তে একটি ফাইল লিখি যা বর্তমান চলমান উদাহরণগুলির একটি পিআইডি রয়েছে contains প্রোগ্রামটি চলাকালীন যদি ফাইলটি বিদ্যমান থাকে তবে ফাইলের পিআইডি আসলে চলছে কিনা তা পরীক্ষা করে (প্রোগ্রামটি ক্র্যাশ হয়ে গেছে বা অন্যথায় পিআইডি ফাইলটি মুছতে ভুলে গেছে)। যদি তা হয় তবে গর্ভপাত বন্ধ করুন। যদি তা না হয় তবে চলমান শুরু করুন এবং পিআইডি ফাইলটি ওভাররাইট করুন।


5

ডিমনটোলস ( http://cr.yp.to/daemontools.html ) এটি করার জন্য ব্যবহৃত বেশ হার্ড-কোর ইউটিলিটিগুলির একটি সেট যা ডিজে বার্নস্টেইন লিখেছেন। আমি এটি কিছু সাফল্যের সাথে ব্যবহার করেছি। এটির বিরক্তিকর অংশটি হ'ল আপনি যখন এগুলি চালাবেন তখন কোনও স্ক্রিপ্টই কোনও দৃশ্যমান ফলাফল দেয় না - কেবল অদৃশ্য রিটার্ন কোডগুলি। তবে একবার এটি চালানো হয় বুলেটপ্রুফ।


হ্যাঁ, আমি একটি এন্ট্রি লিখতে যাচ্ছিলাম যা ডেমোনটুলগুলিও ব্যবহার করে। আমি আমার নিজের পোস্টটি লিখব, কারণ আমি আমার উত্তরের সাথে আরও বিস্তৃত হওয়ার আশা করছি এবং সেইভাবে অনুগ্রহ পাব বলে আশা করি। আমরা দেখব. :-)
ক্রিস জেস্টার-ইয়াং

3

প্রথমে http://code.activestate.com/recips/278731/createDaemon() থেকে পান

তারপরে মূল কোড:

import subprocess
import sys
import time

createDaemon()

while True:
    subprocess.call(" ".join(sys.argv[1:]),shell=True)
    time.sleep(10)

ওহ, ধন্যবাদ! এটিকে কিছুটা আরও সাধারণ করে তুলতে চান যাতে আপনি "ডিমনাইজ foo আরগ 1 আরগ 2" পাশাপাশি "ডিমনাইজ 'ফু আরজি 1 আরজি 2'" করতে পারেন?
শুক্রবার 8:48

ঠিক আছে, এটি এখন যুক্তিতে যোগ দেবে - তবে আপনি যদি নিজের যুক্তিগুলির মধ্যে কখনও ফাঁক রাখতে চান তবে আপনাকে এটি পরিবর্তন করতে হবে।
ডগলাস লিডার

ধন্যবাদ ডগলাস! যদিও এখানে একটি বড় ত্রুটি রয়েছে: "ডেমোনাইজ ফু" চালানো দু'বার অনুলিপি চলতে শুরু করে।

আপনি কিছু পিআইডি রেকর্ডিং কোড যুক্ত করতে পারেন তবে কেবল একবার স্ক্রিপ্টটি চালানো ভাল হবে ...
ডগলাস লিডার

আমি এটিকে পুরো "ডিমনাইজ" র‍্যাপার ধারণার জন্য মৌলিক হিসাবে ভাবা। (যেমন, আমি সর্বদা এটি চালিত হয় তা নিশ্চিত করার জন্য আমি এটি ঘন্টা বা মিনিটের মধ্যে ক্রোন করতে পারি)) আমি কি এই ভুলটি ভাবছি? তৈরিডেমন ইতিমধ্যে কোনওভাবে গ্যারান্টি দেয়? রিবুটের পরে কী হবে?
অপরাহ্ন

1

এটি একটি কার্যকারী সংস্করণ যা একটি খালি ডিরেক্টরিতে অনুলিপি করে দেখতে পারেন ( সিপিএএন নির্ভরতা ইনস্টল করার পরে, যা getopt :: লং , ফাইল :: স্পেস , ফাইল :: পিড এবং আইপিসি :: সিস্টেম: : সরল - সমস্ত সুন্দর মানক এবং যে কোনও হ্যাকারের জন্য অত্যন্ত প্রস্তাবিত: আপনি সেগুলি একবারে ইনস্টল করতে পারেন cpan <modulename> <modulename> ...)।


কিপলাইভ.পিএল:

#!/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);

উদাহরণ.পিএল:

#!/usr/bin/perl

use strict;
use warnings;

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

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

এখন আপনি উপরের উদাহরণটির সাথে অনুরোধ করতে পারেন: ./keepAlive.pl --pidfile=pidfile --command=./example.pl 1 2 3এবং ফাইলটি pidfileতৈরি করা হবে এবং আপনি আউটপুটটি দেখতে পাবেন:

Pid <random number here> no longer running; restarting ./example.pl 1 2 3
./example.pl got arguments: 1 2 3

আমি বিশ্বাস করি এটি সঠিকভাবে বুঝতে না পারলে এটি অনুমান করার মতো নয়। আপনার সমাধানে (ধন্যবাদ, বিটিডাব্লু!) আপনি যে প্রোগ্রামটি ডেমনাইজ করতে চান তা পিআইডি ফাইলটিতে তার পিআইডি লিখতে পরিবর্তন করতে হবে। আমি এমন একটি ইউটিলিটির জন্য প্রত্যাশা করছি যা একটি স্বেচ্ছাসেবী স্ক্রিপ্টটিকে ডেমোনাইজ করতে পারে।
1010

@ ড্রিভস: হ্যাঁ, তবে এর চারপাশে দুটি উপায় রয়েছে: ১. KeepAlive.pl (উদাহরণস্বরূপ.পিএল) দ্বারা লিখিত স্ক্রিপ্টটি আসল প্রোগ্রামটি সম্পাদন করার জন্য কেবল একটি মোড়কের সাহায্যে হতে পারে, বা ২.কিপএলাইভ.পিএল এর সারণিকে পার্স করতে পারে সক্রিয় সিস্টেম প্রক্রিয়াগুলি (সিপিএএন'র প্রোক :: প্রসেসটিটেবল সহ) প্রাসঙ্গিক প্রক্রিয়া এবং এর পিড সন্ধানের চেষ্টা করার জন্য)।
ইথার

1

আপনি মনিটও চেষ্টা করে দেখতে পারেন । মনিট এমন একটি পরিষেবা যা পর্যবেক্ষণ করে এবং অন্যান্য পরিষেবাদির প্রতিবেদন দেয়। এটি মূলত রানটাইম সমস্যা সম্পর্কে অবহিত করার জন্য (ইমেল এবং এসএমএসের মাধ্যমে) উপায় হিসাবে ব্যবহার করা হলেও, এখানে অন্যান্য পরামর্শগুলির বেশিরভাগই যেভাবে সমর্থন করেছেন তা করতে পারে। এটি প্রোগ্রামগুলি স্বয়ংক্রিয়ভাবে (পুনরায়) শুরু করতে বা থামাতে, ইমেলগুলি প্রেরণ করতে, অন্যান্য স্ক্রিপ্টগুলি শুরু করতে এবং আপনার চয়ন করতে পারে এমন আউটপুট লগ রাখতে পারে। উপরন্তু, আমি দৃ document় ডকুমেন্টেশন আছে যেহেতু এটি ইনস্টল করা এবং রক্ষণাবেক্ষণ করা সহজ found


1

আপনি অমর করার চেষ্টা করতে পারেন এটি একটি * নিক্স ক্রস-প্ল্যাটফর্ম (ওএস অগ্নোস্টিক) সুপারভাইজার।

ম্যাকোসটিতে দ্রুত চেষ্টা করার জন্য:

brew install immortal

আপনি যদি বন্দরগুলি থেকে ফ্রিবিএসডি ব্যবহার করছেন বা পিকেজি ব্যবহার করে:

pkg install immortal

পূর্বনির্ধারিত বাইনারিগুলি বা উত্স থেকে ডাউনলোড করে লিনাক্সের জন্য : https://immortal.run/source/

আপনি হয় এটির মতো ব্যবহার করতে পারেন:

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

অথবা একটি কনফিগারেশন ওয়াইএএমএল ফাইল দ্বারা যা আপনাকে আরও বিকল্প দেয়, উদাহরণস্বরূপ:

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

আপনি যদি পৃথক ফাইলে স্ট্যান্ডার্ড ত্রুটির আউটপুট রাখতে চান তবে আপনি এর মতো কিছু ব্যবহার করতে পারেন:

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

0

অন্য উত্তরে আমি ধারাবাহিক উন্নতি করেছি ।

  1. এই স্ক্রিপ্টটির বাইরে স্ট্যান্ডআউটটি নিখুঁতভাবে তার শিশু থেকে আসা স্ট্যান্ডআউট দিয়ে তৈরি হয়েছে যদিও কমান্ডটি ইতিমধ্যে চালু রয়েছে তা সনাক্ত করার কারণে এটি প্রস্থান করে না
  2. সমাপ্ত হলে এর পিডফিলের পরে পরিষ্কার হয়
  3. configচ্ছিক কনফিগারযোগ্য সময়সীমা (কোনও ধনাত্মক সংখ্যাসূচক যুক্তি গ্রহণ করে, প্রেরণ করে sleep)
  4. ব্যবহার প্রম্পট চালু -h
  5. একক কমান্ড প্রয়োগের পরিবর্তে নির্বিচার কমান্ড প্রয়োগ। সর্বশেষ আরগ বা অবশিষ্ট আরগগুলি (যদি একাধিক শেষ আরগের জন্য) প্রেরণ করা হয় eval, সুতরাং আপনি এই স্ক্রিপ্টটিতে এটি ডিমনাইজ করার জন্য একটি স্ট্রিং হিসাবে কোনও শেল স্ক্রিপ্ট তৈরি করতে পারেন it
  6. -ltপরিবর্তে যুক্ত আর্গুমেন্ট গণনা তুলনা<

লিপিটি এখানে:

#!/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

ব্যবহার:

$ 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

সাবধান হন যে আপনি যদি এই স্ক্রিপ্টটি বিভিন্ন ডিরেক্টরি থেকে চালনা করেন তবে এটি বিভিন্ন pidfiles ব্যবহার করতে পারে এবং কোনও চলমান দৃষ্টান্ত সনাক্ত করতে পারে না। যেহেতু এটি আর্গুমেন্টের মাধ্যমে প্রদত্ত অল্পকালীন কমান্ডগুলি চালিত ও পুনরায় চালু করার জন্য তৈরি করা হয়েছে কারণ কিছু ইতিমধ্যে শুরু হয়েছে কিনা তা জানার উপায় নেই, কারণ এটি একই আদেশ বা না তা কে বলবে ? কেবলমাত্র কোনও কিছুর একক দৃষ্টান্ত প্রয়োগ করে এই প্রয়োগের উন্নতির জন্য, পরিস্থিতি সম্পর্কিত একটি সমাধান প্রয়োজন।

এছাড়াও, এটি যথাযথ ডেমন হিসাবে কাজ করার জন্য, আপনাকে অবশ্যই অন্যান্য উত্তরের উল্লেখ হিসাবে ন্যুপ (ন্যূনতম সর্বনিম্ন) ব্যবহার করতে হবে। প্রক্রিয়াটি পেতে পারে এমন সংকেতগুলিতে কোনও স্থিতিস্থাপকতা দেওয়ার জন্য আমি কোনও প্রচেষ্টা করি নি।

আরও একটি বিষয় খেয়াল করার বিষয় হ'ল এই স্ক্রিপ্টটি হত্যা করা (যদি এটি হত্যা করা হয় এমন কোনও অন্য স্ক্রিপ্ট থেকে বলা হয়েছিল বা একটি সংকেত দিয়ে) শিশুটিকে হত্যা করতে সফল না হতে পারে, বিশেষত যদি শিশুটি অন্য কোনও লিপি হয়। আমি কেন এটি অনিশ্চিত, তবে এটি evalকাজ করার সাথে সম্পর্কিত কিছু বলে মনে হচ্ছে যা আমার কাছে রহস্যজনক। সুতরাং সেই উত্তরটি এমন কিছু দিয়ে প্রতিস্থাপন করা বুদ্ধিমান হতে পারে যা অন্য উত্তরের মতো কেবল একটি একক আদেশ গ্রহণ করে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.