systemd-এর ইতিহাস এবং কেন সব Linux distro এটি নিল
SysV init-এর dependency ও process tracking সীমাবদ্ধতা, Upstart ও launchd-এর আগের চেষ্টা, মাত্র চার বছরে systemd গ্রহণ এবং সঠিক আপত্তিগুলো জানুন।
systemd কেন সফল হয়েছে
systemd-এর ইতিহাস শুরু হয় দুটি সীমাবদ্ধতা দিয়ে, যেগুলো SysV init সমাধান করতে পারেনি। SysV init (System V init, AT&T Unix থেকে Linux-এ উত্তরাধিকারসূত্রে আসা startup system) কোনো service কোন কোন service-এর ওপর নির্ভর করে তা বর্ণনা করতে পারত না। Service চালু হওয়ার পর কোন process-গুলো সেই service-এর অন্তর্ভুক্ত, সেটিও জানার কোনো উপায় ছিল না। systemd এমন kernel feature ব্যবহার করে উভয় সমস্যার সমাধান করে, যেগুলো shell script ব্যবহার করতে পারে না: process track করার জন্য control group এবং service-এর ক্রম নির্ধারণের জন্য আগে থেকে খোলা listening socket। এরপর এই দুটি সমাধান কীভাবে userland-এর অন্যান্য অংশে ছড়িয়ে পড়ে, সেটিই এই ইতিহাসের পরবর্তী অংশ। আপত্তিগুলো সেখান থেকেই শুরু হয়, এবং সেগুলোর কয়েকটি যথার্থও ছিল।
SysV init আসলে যা করত
SysV সিস্টেমে PID 1 (process ID 1, kernel যে প্রথম process চালু করে) /etc/inittab পড়ত, একটি runlevel নির্বাচন করত এবং সেই runlevel-এর script চালাত। Script-গুলো /etc/init.d/-এ থাকত। /etc/rc3.d/-এর symbolic link-গুলো নির্ধারণ করত কোন script চলবে এবং কোন ক্রমে চলবে। তাই /etc/rc3.d/S20nginx, /etc/init.d/nginx-এর দিকে নির্দেশ করত এবং এটিকে start argument দিয়ে চালানো হতো।
#!/bin/sh
### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
### END INIT INFO
case "$1" in
start)
start-stop-daemon --start --quiet --pidfile /run/nginx.pid \
--exec /usr/sbin/nginx
;;
stop)
start-stop-daemon --stop --quiet --retry TERM/30/KILL/5 \
--pidfile /run/nginx.pid
;;
esacS20nginx-এর 20 একটি অবস্থান নির্দেশ করে, dependency নয়। এর অর্থ হলো এই script S19-এর পরে এবং S21-এর আগে চলবে। এটি কেন এমন হবে তা জানায় না। তাই কোনো কিছু এটি যাচাই করতে পারে না। আবার কোনো administrator নিরাপদ বলে নির্ধারণ না করলে, সম্পর্কহীন দুটি script একই সময়ে নিরাপদে চালানোও যায় না।
rc program প্রতিটি script ক্রমানুসারে চালাত এবং সেটি exit করা পর্যন্ত অপেক্ষা করত। কোনো script network address-এর জন্য ত্রিশ সেকেন্ড অপেক্ষা করে আটকে থাকলে পুরো boot প্রক্রিয়া ত্রিশ সেকেন্ড আটকে থাকত। এমন service-এর ক্ষেত্রেও এটি ঘটত, যেগুলো কখনো network ব্যবহার করে না।
সেই script-এর শুরুতে থাকা LSB (Linux Standard Base) header ভেতর থেকে এই সমস্যা সমাধানের একটি প্রচেষ্টা ছিল। 2011 সালে Debian 6.0 insserv-কে default করে। এটি প্রতিটি script থেকে Required-Start পড়ে, একটি graph তৈরি করে এবং symbolic link-গুলোর নম্বর নতুন করে নির্ধারণ করত। এরপর Debian startpar ব্যবহার করে স্বাধীন script-গুলো একই সময়ে চালাতে পারত। এতে কিছুটা উন্নতি হয়েছিল, কিন্তু মূল সমস্যার সমাধান হয়নি। Dependency তখনও script exit করার ওপর নির্ভর করত। S20nginx-এর 0 return value মানে একটি shell function সফলভাবে return করেছে। এর অর্থ nginx connection গ্রহণ করছে না।
init script কোনোভাবেই যে পাঁচটি সমস্যা সমাধান করতে পারত না
- Parallel startup। Filename অনুযায়ী ordering করলে মেশিনের প্রতিটি service-এর মধ্যে একটি সম্পূর্ণ ক্রম তৈরি হয়। তাই boot-এর সময় সব কাজের সময়ের যোগফলের সমান হয়ে যায়।
- Readiness। Start script daemon-কে fork করার পরই শেষ হয়; daemon কখন request পরিবেশন করতে পারবে, তখন নয়। তাই পরের script প্রায়ই অতিরিক্ত তাড়াতাড়ি শুরু হয়।
- Supervision। একটি daemon দুইবার fork করে এবং তার parent প্রক্রিয়া শেষ হয়ে যায়। এতে daemon terminal থেকে বিচ্ছিন্ন হয় এবং তাকে PID 1-এর কাছে reparent করা হয়। init একটি child প্রক্রিয়া শেষ হয়েছে তা দেখতে পায়, কিন্তু টিকে থাকা প্রক্রিয়াটির সঙ্গে তার নির্ভরযোগ্য কোনো সংযোগ থাকে না।
- On-demand start। inetd (internet super-server) কোনো connection এলে daemon চালু করতে পারত। কিন্তু এটি ছিল নিজস্ব configuration file-সহ একটি আলাদা system, এবং boot-এর সময় অন্য service-গুলোর ordering নিয়ে এটি কিছুই করত না।
- Resource control। কোনো init script কোনো service-এর memory usage বা CPU-এর অংশ সীমাবদ্ধ করতে পারত না।
ulimitএকটি মাত্র process-এর ক্ষেত্রে প্রযোজ্য ছিল, আরniceশুধু scheduler-এ প্রভাব ফেলত। তাই কোনো service-এর নিয়ন্ত্রণের বাইরে চলে যাওয়া child process-কে মেশিনের অন্য যেকোনো process-এর মতোই দেখা হতো।
Supervision-এর এই ঘাটতিই দৈনন্দিন ব্যবহারে সবচেয়ে বেশি সমস্যা তৈরি করত। PID file ছিল এর workaround। Daemon তার process ID /run/nginx.pid-এ লিখত, আর stop function সেই file পড়ে নিত। Daemon-কে জোর করে kill করা হলে file-টি থেকে যেত। এরপর kernel সেই number অন্য কোনো কিছুর জন্য পুনরায় ব্যবহার করত, এবং start-stop-daemon --stop --pidfile তখন ওই number-এর বর্তমান মালিক process-এ signal পাঠাত। একটি পুরোনো PID file-এর কারণেই init script ভুল process kill করে।
launchd প্রথমেই socket সমস্যার সমাধান করেছিল
Apple 2005 সালে Mac OS X 10.4-এ Dave Zarzycki-এর লেখা launchd প্রকাশ করে। একটি process init, rc, xinetd, crond এবং watchdogd-এর কাজ প্রতিস্থাপন করে।
অনুকরণ করার মতো ধারণাটি ছিল socket activation। launchd প্রথমে প্রতিটি listening socket তৈরি করে, তারপর daemon-গুলো শুরু করে। কোনো daemon এখনও শুরু না হলেও কোনো client সেটিতে সংযোগ করলে connection refused পায় না। কারণ daemon accept() কল না করা পর্যন্ত kernel ওই socket-এর backlog queue-তে সংযোগটি ধরে রাখে। দুটি daemon-এর মধ্যে ordering আর কোনো administrator-কে নির্ধারণ করতে হয় না। socket নিজেই তা পরিচালনা করে।
launchd Mach IPC (inter-process communication)-এর ওপর তৈরি হয়েছিল। এটি Apple-এর XNU kernel-এর অংশ এবং Linux-এ এর কোনো সমতুল্য নেই। তাই code port করা বাস্তবসম্মত ছিল না। তবু ধারণাটি ছড়িয়ে পড়ে।
Upstart-এ event-ই কাজের একক ছিল
Scott James Remnant লিখিত Canonical-এর Upstart 2006 সালের October মাসে Ubuntu 6.10-এ প্রকাশিত হয়। Fedora 9 থেকে Fedora 14 পর্যন্ত এটি ব্যবহৃত হয়েছে। RHEL 6 এবং Chrome OS-এও এটি ব্যবহৃত হয়েছে। Upstart runlevel-এর পরিবর্তে event ব্যবহার করত। একটি job নির্ধারণ করত কোন event সেটিকে start ও stop করবে।
# /etc/init/example.conf
start on filesystem and net-device-up IFACE!=lo
stop on runlevel [!2345]
respawn
respawn limit 10 5
exec /usr/local/bin/exampledjob-এর সংখ্যা বাড়ার সঙ্গে দুটি সমস্যা দেখা দেয়। প্রথমটি ছিল নির্ভরতার দিক নিয়ে। একটি job বলে, “এটি ঘটলে আমাকে start করুন।” ফলে কোন কিছুর ওপর কোনটি নির্ভর করে, সেই তথ্য ভুল ফাইলে থাকে। একটি service জানে তার কী প্রয়োজন। কিন্তু পরের বছর কোন service-এর এটি প্রয়োজন হবে, তা সে জানতে পারে না। নতুন service যোগ করতে প্রায়ই বিদ্যমান job সম্পাদনা করে নতুন event emit করতে হতো।
দ্বিতীয়টি ছিল tracking। Upstart fork করা daemon অনুসরণ করতে fork() call-এর সংখ্যা ptrace দিয়ে গণনা করত। এটি expect fork অথবা expect daemon হিসেবে configure করা হতো। fork-এর সংখ্যা ভুল নির্ধারণ করলে Upstart ইতিমধ্যে exit করা process supervise করত, অথবা ইতিমধ্যে সম্পন্ন হওয়া fork-এর জন্য অপেক্ষা করত। এর লক্ষণ হলো initctl start কোনো error ছাড়াই hang করে থাকা। job file থেকে এর কারণ ব্যাখ্যা করার কোনো উপায় পাওয়া যেত না।
Upstart-এ contributors-দের Canonical-এর contributor agreement-এ স্বাক্ষর করাও বাধ্যতামূলক ছিল। এটি engineering fault ছিল না। তবে কারা এতে কাজ করতেন, তার ওপর এর প্রভাব পড়েছিল।
PID 1 নিয়ে নতুনভাবে ভাবনা, April 2010
30 April 2010-এ Lennart Poettering "Rethinking PID 1" নামে একটি পোস্ট প্রকাশ করেন। Kay Sievers তাঁর সঙ্গে এই প্রকল্পে কাজ করেন। যুক্তিটি চারটি অংশে বিভক্ত ছিল।
- কম জিনিস start করুন। কোনো service-এর জন্য বাস্তবে অনুরোধ না আসা পর্যন্ত অনেক service অপেক্ষা করতে পারে।
- socket দিয়ে ক্রম নির্ধারণ করা সম্ভব হলে আলাদাভাবে ক্রম ঘোষণা করবেন না। এক ধাপে সব socket open করুন, তারপর একসঙ্গে সব start করুন।
- PID file-এর পরিবর্তে control group ব্যবহার করে process track করুন।
- একটি declarative file-এ service বর্ণনা করুন, যাতে একই বর্ণনা প্রতিটি distribution-এ কাজ করে।
সেই বছরই প্রথম release প্রকাশিত হয়। Fedora 14 November 2010-এ systemd-কে একটি option হিসেবে release করে, এবং Fedora 15 May 2011-এ এটিকে default করে।
supervision নির্ভরযোগ্য হওয়ার পেছনে cgroups-এর ভূমিকা
একটি cgroup (control group) হলো process group করার জন্য kernel-এর একটি বৈশিষ্ট্য, যা 2008 সালে Linux 2.6.24-এ যুক্ত হয়। systemd প্রতিটি service-কে নিজস্ব cgroup-এ রাখে। একটি child তার parent-এর cgroup উত্তরাধিকারসূত্রে পায়, এবং unprivileged process নিজেকে কোনো cgroup-এর বাইরে সরাতে পারে না। তাই double forking করেও কিছু গোপন করা যায় না: PID 1 সব সময় একটি unit-এর অন্তর্ভুক্ত process-গুলোর সঠিক সেট ধরে রাখে। কোনো service বন্ধ করার অর্থ হলো তার cgroup-এর সব process kill করা, যা default KillMode=control-group করে।
systemctl status সেই group-টি দেখায়:
● nginx.service - A high performance web server and a reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Tue 2026-08-11 09:14:22 UTC; 3min ago
Main PID: 1042 (nginx)
Tasks: 3 (limit: 4653)
Memory: 6.1M (peak: 7.4M)
CGroup: /system.slice/nginx.service
├─1042 "nginx: master process /usr/sbin/nginx"
├─1043 "nginx: worker process"
└─1044 "nginx: worker process"stale PID file সমস্যার সম্পূর্ণ সমাধান ওই block-এই রয়েছে। কোনো file stale হওয়ার সুযোগ নেই, কারণ তালিকাটি kernel state থেকে আসে।
একই tree limits-ও বহন করে, কারণ tracking-এর জন্য ব্যবহারের আগে accounting-এর উদ্দেশ্যেই cgroups তৈরি করা হয়েছিল। MemoryMax=, CPUQuota= এবং TasksMax= প্রতিটি এক লাইনের নির্দেশনা। কোনো service-এ hard memory ও CPU cap বসানো এখন একটি drop-in file তৈরির কাজ; 2009 সালে এটি এমন একটি shell script-এ patch যোগ করার বিষয় ছিল, যা কেউ লিখত না।
2011 থেকে 2015 সালের মধ্যে প্রতিটি distribution কেন পরিবর্তন করল
- Fedora 15, May 2011।
- openSUSE 12.1, November 2011।
- Mageia 2, May 2012।
- Arch Linux, October 2012 থেকে নতুন installation-এর জন্য default।
- RHEL 7, June 2014।
- SLES 12, October 2014।
- Debian 8, April 2015।
- Ubuntu 15.04, April 2015।
কারণগুলো বেশিরভাগই সাধারণ ছিল। তাই পরিবর্তন দ্রুত হয়েছিল।
- একটি unit file সব distribution-এ কাজ করে। তাই upstream project-গুলো
.servicefile সরবরাহ করা শুরু করে। Distribution-গুলোও প্রতি release-এ প্রতিটি package-এর জন্য আলাদা shell script রক্ষণাবেক্ষণ বন্ধ করে। - 2012 সালের দিকে ConsoleKit-এর রক্ষণাবেক্ষণ বন্ধ হওয়ার পর desktop session tracking
systemd-logind-এ চলে যায়। GNOME-এর logind প্রয়োজন ছিল। তাই systemd ছাড়া কোনো distribution-কে বিকল্প ব্যবস্থা খুঁজতে হতো। সেই বিকল্প,elogind, systemd-এর logind আলাদা করে রক্ষণাবেক্ষণ করা সংস্করণ। - Device manager udev-কে April 2012-এ systemd-এর source tree-তে একীভূত করা হয়। udev সরবরাহ করা distribution-গুলো তখন systemd-এর repository অনুসরণ করতে শুরু করে। এর প্রতিক্রিয়ায় Gentoo
eudevfork করে। - Container-এর কারণে নির্ভরযোগ্য process tracking এবং প্রতি-service limit আরও গুরুত্বপূর্ণ হয়ে ওঠে। দুটিই cgroup-এর বৈশিষ্ট্য। কোনো container process-এর নিয়ন্ত্রণ কোন supervisor-এর হাতে থাকবে, সেই প্রশ্নটি এখনও প্রাসঙ্গিক, বিশেষ করে যখন আপনি reboot-এর পরে Docker Compose stack আবার চালু করেন।
Debian-এর সিদ্ধান্তটিই সবচেয়ে বেশি আলোচনার সৃষ্টি করে। Technical Committee February 2014-এ ভোট দেয়। ভোট সমান হয়। Chair Bdale Garbee systemd-এর পক্ষে সিদ্ধান্তমূলক ভোট দেন। এর কয়েক দিন পরে Ubuntu ঘোষণা করে যে তারা Upstart চালিয়ে যাওয়ার পরিবর্তে Debian-এর সিদ্ধান্ত অনুসরণ করবে। Debian-এর একদল developer November 2014-এ distribution-টি fork করে Devuan তৈরি করেন। Devuan 1.0 প্রকাশিত হয় May 2017-এ।
আপত্তিগুলো যথাযথভাবে উপস্থাপন
পরিধি। এখন একটি প্রকল্পই PID 1, logging daemon, login session management, device manager, network configuration daemon, DNS (domain name system) resolver, NTP (network time protocol) client, container runner এবং boot loader সরবরাহ করে। এগুলো আলাদা binary, তাই আপনাকে সেগুলো install করতেই হবে না—এই প্রচলিত প্রতিরক্ষা বক্তব্যটি সত্য। কিন্তু এটি আপত্তির উত্তর নয়। কোনো desktop-এর logind প্রয়োজন হলে এবং logind systemd-এর tree থেকে release হলে পছন্দ আর স্বাধীন থাকে না। যুক্তিতে coupling বলতে এটাই বোঝানো হয়েছিল, এবং বাস্তবে সেটিই ঘটেছে।
Binary journal। journald plain text-এর পরিবর্তে indexed binary format-এ লেখে। এতে text log-এ আগে না থাকা কিছু সুবিধা পাওয়া যায়: per-unit এবং per-priority filtering, structured field, এবং এমন metadata যা sending program জাল করতে পারে না, কারণ journald নিজেই unit ও cgroup record করে। journalctl -u nginx -p err --since "-1h" grep-এর পরিবর্তে date regular expression ব্যবহার করে। এর খরচও বাস্তব। যে machine boot হয় না, সেখানে rescue shell থেকে less দিয়ে log পড়তে পারবেন না। এর পরিবর্তে mounted disk-টি journalctl-এর কাছে নির্দিষ্ট করুন:
sudo journalctl --directory /mnt/var/log/journal --boot -1 --priority errএখানে আরেকটি সমস্যা আছে, যা মানুষ সাধারণত একবারের পর বুঝতে পারে। /run/log/journal-এ journald log রাখে, যা memory; তবে /var/log/journal থাকলে ভিন্ন বিষয়। যে machine-এ এটি নেই, সেখানে reboot-এর পরে journalctl -b -1 দেখানোর মতো কিছু পায় না। অথচ তখনই আপনি log দেখতে চান। এটি পরীক্ষা করে ঠিক করুন:
journalctl --disk-usage
sudo mkdir -p /var/log/journal
sudo systemd-tmpfiles --create --prefix /var/log/journal
sudo systemctl restart systemd-journaldএখন journalctl --disk-usage-এর /var/log/journal-এর অধীনে archived journal report করা উচিত। plain text-ও চাইলে /etc/systemd/journald.conf-এ ForwardToSyslog=yes সেট করুন এবং rsyslog installed রাখুন।
Boot-এর debugging। কোনো unit আটকে গেলে console-এ একটি line দেখা যায়, আর কিছু নয়:
[ *** ] A start job is running for Wait for Network to be Configured (1min 32s / no limit)আরও অনুসন্ধানের জন্য tool আছে: এটি আটকে থাকা অবস্থায় systemctl list-jobs, পরে systemd-analyze blame এবং systemd-analyze critical-chain, এবং kernel command line-এ systemd.log_level=debug। আপত্তিটির ন্যায্য রূপ হলো, sh জানা যে কেউ init script শুরু থেকে শেষ পর্যন্ত পড়তে পারত। কিন্তু আটকে থাকা unit বিশ্লেষণ করতে কোন এক ডজন command ব্যবহার করতে হবে, তা জানতে হয়। এটি বাস্তব খরচ। প্রত্যেক administrator-এর জন্য এই খরচ একবার দিতে হয়, এবং একই সময়ে অনেক administrator-কে তা দিতে হয়েছিল।
সবার জন্য পরিবর্তিত একটি default। 2016 সালে systemd 230 logout-এর সময় অবশিষ্ট user process kill করার জন্য logind-এর default পরিবর্তন করে। Detached tmux এবং screen session, যেই session সেগুলো শুরু করেছিল সেটি শেষ হলে বন্ধ হয়ে যেত। Distribution-গুলো KillUserProcesses=no-কে /etc/systemd/logind.conf-এ ship করে, এবং supported উত্তর হলো loginctl enable-linger <user>। একটি প্রকল্পের একটি default এমন একটি অভ্যাস পরিবর্তন করেছিল, যার ওপর millions of people নির্ভর করত। বাস্তবে “এক জায়গায় userland-এর অতিরিক্ত অংশ থাকা” বলতে এটাই বোঝায়।
একটি default dependency security surface তৈরি করে। March 2024-এ xz-utils-এর backdoor Debian এবং Ubuntu-র sshd লক্ষ্য করেছিল। Upstream OpenSSH libsystemd-এর সঙ্গে link করে না। sshd যাতে systemd-কে readiness জানাতে পারে, সে জন্য ওই distribution-গুলো এতে patch যোগ করেছিল। এর ফলে libsystemd, এবং তার মাধ্যমে liblzma, dependency হিসেবে যুক্ত হয়েছিল; backdoor-টি liblzma-তেই ছিল। Readiness protocol নিজেই $NOTIFY_SOCKET-এ নির্দিষ্ট socket-এ পাঠানো একটি single datagram। তাই এর জন্য কখনো library প্রয়োজন ছিল না। systemd-এর প্রতিক্রিয়া ছিল dlopen দিয়ে compression library load করা, যাতে সেগুলো আর default হিসেবে linked না হয়। একই ধরনের bug-এর আরেকটি উদাহরণ আছে। 2017 সালে digit দিয়ে শুরু হওয়া একটি User= value invalid হিসেবে গণ্য না হয়ে unit-টি root হিসেবে চলেছিল, fail করেনি। ফলে একটি typo privilege escalation-এ পরিণত হয়। পরবর্তী version-গুলো unit start করতে অস্বীকার করে।
systemctl prompt-এ নিজের সিস্টেমের ইতিহাস
উপরের প্রতিটি সমস্যা এখন এমন একটি directive, যা আপনি একটি ফাইলে পড়তে পারবেন।
- Serial boot হয়ে গেল
After=এবংWants=, আরsystemd-analyze critical-chainদেখায় আসলে কোন কারণে আপনার boot আটকে ছিল। - Readiness হয়ে গেল
Type=notify, যেখানে service পরিবেশন করতে পারলেREADY=1লিখে$NOTIFY_SOCKET-এ। পুরনো daemon-এর জন্যType=forkingএবংPIDFile=এখনও আছে, আর PID file কখনও তৈরি না হলে এই typestart operation timed out. Terminating.দিয়ে ব্যর্থ হয়। - Supervision cgroup-এর দায়িত্বে চলে গেল। তাই
RestartSec=-সহRestart=on-failurewrapper script-এর বদলে ব্যবহৃত হয়, আরStartLimitBurst=crash loop-কে অনন্তকাল চলতে বাধা দেয়। - inetd-এর জায়গায়
.serviceunit-এর পাশে একটি.socketunit থাকে। ulimitএখনMemoryMax=,CPUQuota=এবংTasksMax=-এ পরিণত হয়েছে।- init script-এর
su - appuser -cline এখনUser=,NoNewPrivileges=yesএবংProtectSystem=strict-এ পরিণত হয়েছে। তাই unprivileged user হিসেবে service চালানো unit-এর স্বাভাবিক কাঠামো, অতিরিক্ত কাজ নয়।
[Unit]
Description=Example API
Wants=network-online.target
After=network-online.target postgresql.service
Requires=postgresql.service
[Service]
Type=notify
ExecStart=/usr/local/bin/exampled
Restart=on-failure
RestartSec=2
MemoryMax=512M
CPUQuota=50%
TasksMax=128
User=exampled
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
StateDirectory=exampled
[Install]
WantedBy=multi-user.targetএই ফাইলের একটি line এমন একটি ভুল, যা সবাই একবার করে। Requires=postgresql.service একটি requirement, ordering নয়। এটি বলে, Postgres ব্যর্থ হলে আপনার unit-ও ব্যর্থ হবে; কিন্তু Postgres আগে start করতে হবে, তা বলে না। After=postgresql.service ছাড়া দুটিই একই সময়ে start হয়, আর আপনার service এমন একটি port-এ সংযোগ করার চেষ্টা করে যেখানে তখনও কোনো process listen করছে না। দুটিকে ইচ্ছাকৃতভাবে আলাদা রাখা হয়েছে, কারণ কখনও আপনি একটিকে অন্যটি ছাড়া ব্যবহার করতে চাইতে পারেন। ProtectSystem=strict এই service-এর জন্য file system read-only হিসেবে mount করে। তাই StateDirectory= আছে: এটি /var/lib-এর অধীনে service-এর জন্য একটি writable path দেয়।
2026 সালের server-এ 2005-এর নকশা দেখার সবচেয়ে পরিষ্কার উদাহরণ হলো Ubuntu 24.04-এ SSH, যেখানে August 2026 অনুযায়ী systemd 255 শিপ করে। ssh.service defaultভাবে socket activated: ssh.socket listening socket ধরে রাখে, আর connection এলে sshd start হয়। তাই /etc/ssh/sshd_config-এর মধ্যে Port 2222-এর কোনো প্রভাব নেই, কারণ port খুলেছে sshd নয়। পরিবর্তনটি socket unit-এ করতে হবে।
sudo systemctl edit ssh.socket[Socket]
ListenStream=
ListenStream=2222খালি ListenStream= packaged unit থেকে উত্তরাধিকারসূত্রে পাওয়া value মুছে দেয়। এটি বাদ দিলে আপনি দুটিই port পাবেন, কারণ systemd list-এ নতুন value যোগ করে; আগের value প্রতিস্থাপন করে না। এরপর apply করে পরীক্ষা করুন। পুরো সময় একটি দ্বিতীয় SSH session খোলা রাখুন:
sudo systemctl daemon-reload
sudo systemctl restart ssh.socket
sudo ss -lntp | grep 2222ss-এ port 2222-এ একটি মাত্র socket দেখা উচিত, যার মালিক systemd; sshd নয়। আপনার VPS-এ বিশ বছর পরে এটিই launchd-এর নকশা। পুরনো আচরণ চাইলে sudo systemctl disable --now ssh.socket-এর পরে sudo systemctl enable --now ssh.service চালান। এতে দীর্ঘসময় চলা sshd তৈরি হবে, যা আবার নিজের config থেকে Port পড়বে।
আপনি কোন বিষয়গুলোর মুখোমুখি হবেন, তা নির্ভর করে আপনার ব্যবহৃত release-এর ওপর। তাই upgrade পরিকল্পনা করার আগে LTS এবং interim Ubuntu release-এর পার্থক্য জানা উপযোগী। একাধিক machine-এ একই unit file সর্বত্র অভিন্ন হওয়ার কারণেই এক জায়গা থেকে একাধিক server পরিচালনা করা এখন configuration problem, shell scripting problem নয়। আর নিজের unit লেখার সময় service এবং timer-এর জোড়া সেই কাজ করে, যা 2009 সালে init script এবং cron line-এর মধ্যে ভাগ করে করতে হতো।
FAQ
Linux distribution-গুলো কেন SysV init-এর পরিবর্তে systemd ব্যবহার শুরু করেছিল?
এর পেছনে দুটি engineering কারণ এবং একটি maintenance কারণ ছিল। SysV init filename অনুযায়ী service-এর ক্রম নির্ধারণ করত। এটি dependency-এর পরিবর্তে অবস্থান ব্যবহার করত। কোনো daemon parent থেকে fork হয়ে আলাদা হয়ে গেলে SysV init সেটির tracking হারিয়ে ফেলত। এ কারণেই পুরোনো PID file ভুল process বন্ধ করে দিতে পারত। systemd socket activation এবং dependency directive ব্যবহার করে ordering সমস্যার সমাধান করে। Control group ব্যবহার করে process tracking সমস্যারও সমাধান করে। Maintenance কারণটি পরিবর্তনের গতি নির্ধারণ করে: প্রতিটি distribution-এ একই unit file কাজ করে। তাই upstream project একটি .service file প্রকাশ করত, এবং distribution maintainer-রা প্রতিটি package-এর জন্য আলাদা shell script লেখা বন্ধ করে। Fedora 15 May 2011-এ পরিবর্তন করে। Ubuntu 15.04 April 2015-এ পরিবর্তন করা শেষ বড় distribution ছিল।
systemd কি একটি বিশাল binary?
না। Source tree থেকে অনেকগুলো পৃথক program build হয়। PID 1 হলো /usr/lib/systemd/systemd। journald, logind এবং udevd পৃথক process হিসেবে নিজেদের binary ব্যবহার করে। আপনার system-এ এগুলো দেখতে ls /usr/lib/systemd/ চালান। যে সমালোচনাটি এখনও রয়েছে, তা binary-এর আকার নিয়ে নয়; release coupling নিয়ে। এই program-গুলো একসঙ্গে release হয় এবং private interface ভাগ করে। তাই distribution-গুলো সাধারণত এগুলোকে একটি set হিসেবে গ্রহণ করে। GNOME-এর মতো software-ও নির্দিষ্টভাবে logind-এর ওপর নির্ভর করতে শুরু করে।
systemd ছাড়া কি এখনও Linux চালানো যায়?
হ্যাঁ। Devuan sysvinit সরবরাহ করে, Gentoo-তে default হিসেবে OpenRC থাকে, Void runit ব্যবহার করে, Alpine busybox init-এর সঙ্গে OpenRC ব্যবহার করে, এবং Slackware BSD style script বজায় রাখে। এর জন্য compatibility সংক্রান্ত অতিরিক্ত কাজ করতে হয়। logind প্রত্যাশা করে এমন desktop software-এর জন্য elogind প্রয়োজন। এটি systemd-এর logind, যা standalone package হিসেবে রক্ষণাবেক্ষণ করা হয়। এছাড়া server software-এর ক্রমবর্ধমান অংশ এখন শুধু একটি .service file প্রকাশ করে। তাই startup script আপনাকেই লিখে রক্ষণাবেক্ষণ করতে হয়।
journal plain text file-এর পরিবর্তে binary কেন?
কারণ journald একটি index সহ structured field সংরক্ষণ করে। এর ফলে per-unit filtering, priority filtering এবং sending program জাল করতে পারে না এমন metadata পাওয়া যায়। journald log line-কে বিশ্বাস না করে নিজেই unit, cgroup এবং প্রকৃত UID record করে। এর বিনিময়ে এটি পড়তে journalctl প্রয়োজন হয়, rescue system থেকেও। সে ক্ষেত্রে mounted disk-টির অবস্থান journalctl --directory /mnt/var/log/journal দিয়ে নির্দিষ্ট করুন। একই সঙ্গে text output চাইলে /etc/systemd/journald.conf-এ ForwardToSyslog=yes সেট করুন।
আমার /etc/init.d script সম্পাদনা করার পরিবর্তে কী ব্যবহার করব?
Drop-in file ব্যবহার করুন। /usr/lib/systemd/system/-এর unit সম্পাদনা করবেন না, কারণ package upgrade সেটি overwrite করে। sudo systemctl edit nginx.service চালালে systemd /etc/systemd/system/nginx.service.d/override.conf তৈরি করে। এই file-টি packaged unit-এর ওপর merge হয়। Merge হওয়া ফলাফল systemctl cat nginx.service দেখায়। Machine-এ থাকা সব override systemd-delta তালিকাভুক্ত করে। হাতে কোনো edit করার পর sudo systemctl daemon-reload চালান। তা না হলে পরের command Warning: The unit file, source configuration file or drop-ins of nginx.service changed on disk. প্রদর্শন করবে।