Exim 4.100 Güncellemesi Sonrası MailScanner Split Spool Sorunu ve Kesin Çözümü
21 Eylül 2026 tarihinde cPanel sunucularımızdan birinde yapılan ana sistem güncellemesinin ardından e-posta kuyruğunun olağan dışı biçimde büyüdüğünü fark ettik.
Sunucuda aşağıdaki yapı çalışıyordu:
CentOS 7.9
cPanel 11.110.0.144
Exim 4.100
MailScanner 5.4.4
Sunucunun CPU, RAM, disk ve inode değerlerinde herhangi bir problem bulunmuyordu. Exim, Dovecot ve MailScanner servisleri de aktif görünüyordu.
Ancak mail kuyruğu kısa sürede yüzlerce mesaja ulaştı.
İlk bakışta sorun aşağıdakilerden biri gibi görünüyordu:
- Spam saldırısı
- Disk doluluğu
- Inode tükenmesi
- Exim servisinin durmuş olması
- Dovecot problemi
- MailScanner servisinin durmuş olması
Gerçek neden bunların hiçbiri değildi.
Belirti: Spool File Not Found Hatası
Mail kuyruğunda bulunan mesajlardan biri manuel olarak teslim edilmeye çalışıldığında Exim aşağıdakine benzer bir hata veriyordu:
Spool file 1x8enJ-000000005pQ-2hdX-D not found
Fakat dosyayı sunucuda aradığımızda gerçekten vardı:
find /var/spool/exim/input -name '1x8enJ-000000005pQ-2hdX-*'
Sonuç:
/var/spool/exim/input/0/1x8enJ-000000005pQ-2hdX-D
/var/spool/exim/input/0/1x8enJ-000000005pQ-2hdX-H
Yani Exim “dosya yok” diyordu ancak dosya fiziksel olarak diskte bulunuyordu.
Burada sorun dosyanın kendisi değil, dosyanın bulunduğu dizindi.
Exim Split Spool Nedir?
Exim büyük mail kuyruklarında bütün mesajları tek klasörde tutmak yerine mesajları alt klasörlere dağıtabilir.
Örneğin:
/var/spool/exim/input/A/
/var/spool/exim/input/B/
/var/spool/exim/input/C/
/var/spool/exim/input/J/
/var/spool/exim/input/N/
Bizim örnek mesaj ID’miz:
1x8enJ-000000005pQ-2hdX
Mesaj ID’sinin ilk bölümü:
1x8enJ
Bu yapıdaki split spool karakteri:
J
olmalıdır.
Dolayısıyla doğru yol:
/var/spool/exim/input/J/1x8enJ-000000005pQ-2hdX-H
olmalıydı.
Fakat MailScanner mesajı şuraya yazıyordu:
/var/spool/exim/input/0/
Bu nedenle Exim mesajı bulamıyordu.
Problemi strace ile Doğrulama
Exim’in dosyayı gerçekte hangi dizinde aradığını görmek için strace kullandık.
strace -f -e trace=openat,stat,lstat,access \
/usr/sbin/exim \
-C /etc/exim_outgoing.conf \
-Mvb 1x8enJ-000000005pQ-2hdX 2>&1 | grep 1x8enJ
Exim’in aradığı yol:
/var/spool/exim/input/J/1x8enJ-000000005pQ-2hdX-D
olmasına rağmen MailScanner dosyayı:
/var/spool/exim/input/0/
dizinine bırakmıştı.
Böylece sorunun split spool hesaplamasında olduğu kesinleşti.
Sunucunun Genel Sağlığını Kontrol Etme
Önce disk, inode ve servislerin düzgün çalışıp çalışmadığını kontrol ettik.
Disk Kullanımı
df -hT / /var /home
Örnek sağlıklı çıktı:
Filesystem Type Size Used Avail Use% Mounted on
/dev/mapper/centos-root xfs 200G 44G 157G 22% /
/dev/mapper/centos-home xfs 541G 315G 227G 59% /home
Inode Kullanımı
df -i / /var /home
Örnek sağlıklı değer:
IUse% 1%
Load Kontrolü
uptime
Örneğin:
load average: 0.21, 0.22, 0.23
Exim Servisi
/scripts/restartsrv_exim --status
Dovecot Servisi
/scripts/restartsrv_dovecot --status
MailScanner Servisi
systemctl status MailScanner --no-pager -l
Sunucuda disk, inode, CPU load ve servis tarafında temel bir problem yoktu.
Güncelleme Sonrası Exim Sürüm Değişikliği
Sunucuda daha önce Exim 4.99.5 çalışıyordu.
cPanel ana güncellemesi sonrasında Exim sürümü:
Exim version 4.100
oldu.
Kontrol:
exim -bV | head -15
Örnek çıktı:
Exim version 4.100 #2 built 10-Sep-2026 11:24:25
Exim binary değişim zamanını görmek için:
stat /usr/sbin/exim
Exim config dosyalarının yeniden oluşturulma zamanını görmek için:
stat /etc/exim.conf
stat /etc/exim_outgoing.conf
Bizim örneğimizde Exim güncellemesi ve config yeniden oluşturma işlemi ile mail kuyruğu sorununun başlaması zaman olarak birebir örtüşüyordu.
MailScanner Sürümünü Kontrol Etme
Bizim kurulumumuzda gerçek MailScanner executable yolu:
/usr/mailscanner/usr/sbin/MailScanner
Sürüm kontrolü:
/usr/mailscanner/usr/sbin/MailScanner --version
Sonuç:
This is MailScanner version 5.4.4
MailScanner Split Exim Spool Ayarını Kontrol Etme
Aktif yapılandırmayı görmek için:
grep -nE '^(Incoming Queue Dir|Outgoing Queue Dir|Split Exim Spool)' \
/usr/mailscanner/etc/MailScanner.conf
Örnek:
Incoming Queue Dir = /var/spool/exim_incoming/input/*
Outgoing Queue Dir = /var/spool/exim/input
Split Exim Spool = yes
Yani problem Split Exim Spool = no değildi.
Ayar zaten doğru şekilde yes durumundaydı.
Sorunlu MailScanner Kodu
İlgili dosyalar:
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStore.pm
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStoreNew.pm
Kodu görmek için:
sed -n '195,230p' \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStore.pm
Eski kod:
sub OutQDir {
my $name = shift;
if (MailScanner::Config::Value('spliteximspool')) {
return '/' . substr($name,-13,1) . '/';
} else {
return '/';
}
}
Diğer dosyada:
sub OutQDir {
my $name = shift;
if (MailScanner::Config::Value('spliteximspool')) {
return '/' . substr($name,-20,1) . '/';
} else {
return '/';
}
}
Asıl Sorun Neydi?
Buradaki problem:
substr($name,-13,1)
ve:
substr($name,-20,1)
ifadelerinin mesaj ID uzunluğunun sabit olduğunu varsaymasıdır.
Exim 4.100 ile kullanılan yeni mesaj ID yapısında bu hesaplama yanlış karakteri seçmeye başladı.
Örnek mesaj:
1x8enJ-000000005pQ-2hdX
Doğru split spool karakteri:
J
olması gerekirken MailScanner:
0
karakterini seçiyordu.
Bu nedenle yüzlerce mesaj:
/var/spool/exim/input/0/
dizinine yazılmıştı.
Acil ve Hızlı Çözüm
Önce mutlaka yedek alınmalıdır.
cp -a \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStore.pm \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStore.pm.bak-20260921
cp -a \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStoreNew.pm \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStoreNew.pm.bak-20260921
Bizim Exim 4.100 yapımızda doğru karakter mesaj ID’nin 6. karakteriydi.
Bu nedenle:
sed -i 's/substr(\$name,-13,1)/substr(\$name,5,1)/' \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStore.pm
sed -i 's/substr(\$name,-20,1)/substr(\$name,5,1)/' \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStoreNew.pm
Kontrol:
grep -nA8 'sub OutQDir' \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStore.pm \
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStoreNew.pm
Beklenen:
return '/' . substr($name,5,1) . '/';
Daha Sağlam ve Geleceğe Dayanıklı Çözüm
Dünya genelinde kullanılabilecek daha güvenli yaklaşım sabit karakter pozisyonuna bağlı kalmamaktır.
Mesaj ID’sinin ilk bölümünün son karakteri dinamik olarak alınabilir.
sub OutQDir {
my $name = shift;
if (MailScanner::Config::Value('spliteximspool')) {
my ($firstpart) = split(/-/, $name, 2);
return '/' . substr($firstpart, -1, 1) . '/';
} else {
return '/';
}
}
Örnek:
1x8enJ-000000005pQ-2hdX
İlk bölüm:
1x8enJ
Son karakter:
J
Bu yaklaşım mesaj ID’sinin geri kalan bölümünün uzunluğundan bağımsızdır.
MailScanner Servisini Yeniden Başlatma
Servis adını kontrol etmek için:
systemctl list-unit-files | grep -i mailscanner
Örnek:
MailScanner.service enabled
Restart:
systemctl restart MailScanner
Kontrol:
systemctl status MailScanner --no-pager -l
Yeni Mail ile Düzeltmeyi Test Etme
MailScanner restart işleminden sonra dışarıdan bir test e-postası gönderin.
Ardından:
find /var/spool/exim/input -type f -name '*-H' \
-newermt '5 minutes ago' \
-printf '%TY-%Tm-%Td %TH:%TM:%TS %h/%f\n' \
| sort | tail -20
Bizim test mesajımız:
1x8gzN-000000003ZG-2Jl6
şu dizine düştü:
/var/spool/exim/input/N/1x8gzN-000000003ZG-2Jl6-H
Mesaj ID’sinin ilk bölümü:
1x8gzN
olduğu için N doğru split spool klasörüdür.
Yanlış Klasördeki Eski Mesajları Sayma
count=0
for h in $(find /var/spool/exim/input -type f -name '*-H'); do
id=$(basename "$h" -H)
actual=$(basename "$(dirname "$h")")
firstpart="${id%%-*}"
expected="${firstpart: -1}"
if [ "$actual" != "$expected" ]; then
count=$((count+1))
fi
done
echo "Yanlış klasörde mesaj: $count"
Bizim sunucumuzda sonuç:
769
mesajdı.
H ve D Dosyalarını Kontrol Etme
Exim spool mesajlarında genellikle:
-H
-D
dosya çifti bulunur.
Taşımadan önce eşleşme kontrol edilmelidir.
both=0
honly=0
while IFS= read -r h; do
id=$(basename "$h" -H)
dir=$(dirname "$h")
actual=$(basename "$dir")
firstpart="${id%%-*}"
expected="${firstpart: -1}"
[ "$actual" = "$expected" ] && continue
if [ -f "$dir/${id}-D" ]; then
both=$((both+1))
else
honly=$((honly+1))
echo "D dosyası eksik: $id"
fi
done < <(
find /var/spool/exim/input \
-type f \
-name '*-H'
)
echo "H+D tam: $both"
echo "D eksik: $honly"
Bizde sonuç:
H+D TAM: 769
SADECE H / D EKSIK: 0
Outgoing Queue Runner'ı Geçici Olarak Durdurma
Dosyaları taşırken outgoing Exim queue runner’ın aynı anda spool dosyalarına erişmesini istemeyiz.
PID:
cat /var/spool/exim/exim-outgoing.pid
Örnek:
5517
Geçici durdurma:
kill -STOP 5517
Kontrol:
ps -o pid,stat,cmd -p 5517
STAT alanında:
Ts
görülmelidir.
Bu işlem prosesi öldürmez. Sadece geçici olarak askıya alır.
Yanlış Spool Dosyalarını Doğru Dizinlere Taşıma
Aşağıdaki script yalnızca yanlış klasörde bulunan mesajları taşır.
BASE=/var/spool/exim/input
moved=0
collision=0
failed=0
while IFS= read -r h; do
id=$(basename "$h" -H)
source_dir=$(dirname "$h")
actual=$(basename "$source_dir")
firstpart="${id%%-*}"
expected="${firstpart: -1}"
[ "$actual" = "$expected" ] && continue
d="$source_dir/${id}-D"
target="$BASE/$expected"
if [ ! -d "$target" ]; then
mkdir -p "$target"
chown --reference="$BASE/0" "$target"
chmod --reference="$BASE/0" "$target"
fi
if [ -e "$target/${id}-H" ] || \
[ -e "$target/${id}-D" ]; then
echo "ÇAKIŞMA: $id"
collision=$((collision+1))
continue
fi
if mv -- "$d" "$h" "$target/"; then
moved=$((moved+1))
else
echo "TAŞIMA HATASI: $id"
failed=$((failed+1))
fi
done < <(
find "$BASE" \
-type f \
-name '*-H'
)
echo "Taşınan : $moved"
echo "Çakışma : $collision"
echo "Hata : $failed"
Bizim sonuç:
TAŞINAN MESAJ : 769
CAKISMA : 0
HATA : 0
Yanlış Mesaj Kalmış mı?
count=0
for h in $(find /var/spool/exim/input -type f -name '*-H'); do
id=$(basename "$h" -H)
actual=$(basename "$(dirname "$h")")
firstpart="${id%%-*}"
expected="${firstpart: -1}"
if [ "$actual" != "$expected" ]; then
echo "$id actual=$actual expected=$expected"
count=$((count+1))
fi
done
echo "YANLIS KALAN: $count"
Bizim sonuç:
YANLIS KALAN: 0
Queue Runner'ı Yeniden Çalıştırma
kill -CONT 5517
Kontrol:
ps -o pid,stat,cmd -p 5517
STAT artık:
Ss
olmalıdır.
Gerçek Outgoing Kuyruğu Kontrol Etme
MailScanner kullanılan yapılarda standart:
exim -bpc
her zaman gerçek outgoing kuyruğunu göstermeyebilir.
Bizim yapımızda outgoing Exim ayrı config ile çalışıyordu:
/etc/exim_outgoing.conf
Dolayısıyla:
/usr/sbin/exim -C /etc/exim_outgoing.conf -bpc
kullanılmalıdır.
Bizim düzeltme sonrası kuyruk:
767
mesajdı.
Queue run:
/usr/sbin/exim -C /etc/exim_outgoing.conf -qff
sonrası kuyruk:
35
mesaja düştü.
Yani yüzlerce mesaj tekrar işlenmeye başladı.
Kalan Mesajların Hepsi Hata Değildir
Kuyruk sıfır olmak zorunda değildir.
Örneğin:
Connection timed out
uzak SMTP sunucusunun cevap vermemesi olabilir.
Veya:
550 5.1.1
The email account that you tried to reach does not exist
geçersiz alıcı olabilir.
Dolayısıyla MailScanner split spool problemi çözüldükten sonra kalan mesajlar ayrıca incelenmelidir.
Yıldız Bilişim MailScanner Update Sistemi
MailScanner bakım işlemlerini WHM içerisinden tek butonla çalıştırabilmek için ayrı bir güncelleme sistemi oluşturulabilir.
Mantık şu adımlardan oluşur:
- ClamAV veritabanını güncelle
- MailScanner phishing listelerini güncelle
- Bad sites listesini güncelle
- Safe sites listesini güncelle
- Bad email ve ScamNailer listelerini güncelle
- SpamAssassin rules update çalıştır
- MailScanner - Exim split spool uyumluluğunu kontrol et
- Gerekirse patch uygula
- MailScanner ve Exim servislerini yeniden başlat
- Tüm işlemleri logla
WHM Force MailScanner Update Butonu
<input type="submit" class="btn btn-default" value="Force MailScanner Update">
Form örneği:
<form method="post" action="">
<input
type="hidden"
name="action"
value="force_update"
>
<input
type="submit"
class="btn btn-default"
value="Force MailScanner Update"
>
</form>
Yıldız MailScanner Update Scripti
Örnek script yolu:
/usr/local/sbin/yildiz-mailscanner-update
Oluşturma:
nano /usr/local/sbin/yildiz-mailscanner-update
İçerik:
#!/bin/bash
set -u
LOG="/var/log/yildiz-mailscanner-update.log"
DATE="$(date '+%Y-%m-%d %H:%M:%S')"
log()
{
echo "$*" | tee -a "$LOG"
}
log "===== $DATE Yildiz MSFE update started ====="
log "[1] ClamAV freshclam"
if command -v freshclam >/dev/null 2>&1; then
freshclam >>"$LOG" 2>&1
log "freshclam exit=$?"
else
log "freshclam not found"
fi
log "[2] MailScanner list updates"
for CMD in \
ms-update-phishing \
ms-update-bad-sites \
ms-update-safe-sites \
ms-update-bad-emails
do
if command -v "$CMD" >/dev/null 2>&1; then
"$CMD" >>"$LOG" 2>&1
RC=$?
log "$CMD exit=$RC"
else
log "$CMD not found"
fi
done
log "[3] SpamAssassin update"
if command -v sa-update >/dev/null 2>&1; then
sa-update >>"$LOG" 2>&1
log "sa-update exit=$?"
fi
if command -v ms-update-sa >/dev/null 2>&1; then
ms-update-sa >>"$LOG" 2>&1
log "ms-update-sa exit=$?"
fi
if command -v ms-update-vs >/dev/null 2>&1; then
ms-update-vs >>"$LOG" 2>&1
log "ms-update-vs exit=$?"
fi
log "[4] Exim MailScanner compatibility check"
FIX="/usr/local/sbin/fix-mailscanner-exim-splitspool"
if [ -x "$FIX" ]; then
"$FIX" >>"$LOG" 2>&1
log "split-spool compatibility check exit=$?"
else
log "split-spool compatibility script not installed"
fi
log "[5] Restart services"
if command -v /scripts/restartsrv_exim >/dev/null 2>&1; then
/scripts/restartsrv_exim >>"$LOG" 2>&1
log "restartsrv_exim exit=$?"
fi
systemctl restart MailScanner >>"$LOG" 2>&1
log "MailScanner restart exit=$?"
DATE="$(date '+%Y-%m-%d %H:%M:%S')"
log "===== $DATE Yildiz MSFE update finished ====="
Yetkiler:
chown root:root /usr/local/sbin/yildiz-mailscanner-update
chmod 700 /usr/local/sbin/yildiz-mailscanner-update
Log dosyası:
/var/log/yildiz-mailscanner-update.log
Otomatik Split Spool Patch Scripti
Örnek dosya:
/usr/local/sbin/fix-mailscanner-exim-splitspool
İçerik:
#!/bin/bash
set -e
BASE="/usr/mailscanner/usr/share/MailScanner/perl/MailScanner"
FILES=(
"$BASE/EximDiskStore.pm"
"$BASE/EximDiskStoreNew.pm"
)
for FILE in "${FILES[@]}"
do
[ -f "$FILE" ] || continue
if grep -q 'substr($firstpart, -1, 1)' "$FILE"; then
echo "$FILE already patched"
continue
fi
cp -a \
"$FILE" \
"$FILE.pre-exim-split-fix-$(date +%Y%m%d-%H%M%S)"
perl -0777 -i -pe '
s{
return\s+
[\x27"]\/[\x27"]\s*\.\s*
substr\(\$name,\s*-\d+,\s*1\)
\s*\.\s*
[\x27"]\/[\x27"];
}{
my (\$firstpart) = split(/-/, \$name, 2);
return "/" . substr(\$firstpart, -1, 1) . "/";
}gx
' "$FILE"
done
Yetki:
chmod 700 /usr/local/sbin/fix-mailscanner-exim-splitspool
WHM CGI Tarafı
Buton yalnızca sabit bir root-owned scripti çalıştırmalıdır.
Kullanıcıdan shell komutu alınmamalıdır.
Örnek Perl:
if (
defined $FORM{'action'}
&&
$FORM{'action'} eq 'force_update'
) {
my $script =
'/usr/local/sbin/yildiz-mailscanner-update';
if (-x $script) {
system($script);
}
}
Aşağıdaki gibi kullanıcıdan komut alıp çalıştırmak güvenli değildir:
system($FORM{'command'});
veya:
shell_exec($_POST['command']);
Güncellemeyi Arka Planda Çalıştırma
CGI isteğini uzun süre açık bırakmamak için child process kullanılabilir.
my $pid = fork();
if (!defined $pid) {
die "fork failed";
}
if ($pid == 0) {
open STDIN, '<', '/dev/null';
open STDOUT, '>>',
'/var/log/yildiz-mailscanner-update.log';
open STDERR, '>&STDOUT';
exec(
'/usr/local/sbin/yildiz-mailscanner-update'
);
exit 1;
}
Son Update Logunu WHM'de Gösterme
my $log =
'/var/log/yildiz-mailscanner-update.log';
if (-f $log) {
open my $fh, '<', $log;
my @lines = <$fh>;
close $fh;
my $start =
@lines > 200
? @lines - 200
: 0;
print "<pre>";
for my $i ($start .. $#lines) {
my $line = $lines[$i];
$line =~ s/&/&/g;
$line =~ s/</</g;
$line =~ s/>/>/g;
print $line;
}
print "</pre>";
}
Log içeriğini doğrudan HTML'e basmak yerine HTML escape yapılması önemlidir.
Güncelleme Sonrası Otomatik Sağlık Testi
echo "===== EXIM ====="
exim -bV | head -3
echo "===== MAILSCANNER ====="
/usr/mailscanner/usr/sbin/MailScanner \
--version 2>&1 | \
grep 'MailScanner version'
echo "===== SPLIT SPOOL CONFIG ====="
grep -n \
'Split Exim Spool' \
/usr/mailscanner/etc/MailScanner.conf
echo "===== WRONG SPOOL CHECK ====="
wrong=0
while IFS= read -r h
do
id=$(basename "$h" -H)
actual=$(basename "$(dirname "$h")")
firstpart="${id%%-*}"
expected="${firstpart: -1}"
if [ "$actual" != "$expected" ]; then
wrong=$((wrong+1))
fi
done < <(
find /var/spool/exim/input \
-type f \
-name '*-H'
)
echo "Wrong spool messages: $wrong"
Normal sonuç:
Wrong spool messages: 0
Otomatik Alarm Sistemi
Kontrol scripti:
nano /usr/local/sbin/check-exim-split-spool
İçerik:
#!/bin/bash
BASE="/var/spool/exim/input"
WRONG=0
while IFS= read -r h
do
id=$(basename "$h" -H)
actual=$(basename "$(dirname "$h")")
firstpart="${id%%-*}"
expected="${firstpart: -1}"
if [ "$actual" != "$expected" ]; then
WRONG=$((WRONG+1))
fi
done < <(
find "$BASE" \
-type f \
-name '*-H'
)
if [ "$WRONG" -gt 0 ]; then
echo "WARNING: $WRONG Exim spool messages are in incorrect directories."
exit 1
fi
exit 0
Yetki:
chmod 700 /usr/local/sbin/check-exim-split-spool
Cron:
*/10 * * * * root /usr/local/sbin/check-exim-split-spool >> /var/log/exim-split-check.log 2>&1
Önemli Güvenlik Notları
- Spool dosyalarını doğrudan silmeyin.
- Taşıma öncesinde mutlaka H ve D dosya çiftlerini kontrol edin.
- MailScanner kaynak dosyalarını değiştirmeden önce yedek alın.
- WHM üzerinden root seviyesinde script çalıştırıyorsanız kullanıcıdan shell komutu almayın.
- Web üzerinden çalışan update sistemini yalnızca yetkili WHM oturumuna açık tutun.
- Doğrudan internete açık update.php benzeri dosyalar oluşturmayın.
- Gerçek WHM session ID değerlerini blog yazısında paylaşmayın.
WHM Session URL Kullanımı
Gerçek URL:
https://SERVER-IP:2087/cpsessXXXXXXXXXX/cgi/configserver/mailscanner.cgi
şeklinde maskelenmelidir.
Gerçek cpsess değerini veya özel yönetim IP'nizi açık şekilde yayınlamayın.
Sonuç
Bu olayda sorun disk, inode, CPU, Dovecot veya klasik bir Exim servis arızası değildi.
Asıl problem:
Exim 4.100
+
MailScanner 5.4.4
+
eski split spool karakter hesaplama yöntemi
kombinasyonuydu.
MailScanner mesajları yanlışlıkla:
/var/spool/exim/input/0/
altına yazıyor, Exim ise mesaj ID’ye göre:
/var/spool/exim/input/J/
/var/spool/exim/input/N/
/var/spool/exim/input/P/
gibi doğru split spool klasörlerini arıyordu.
MailScanner kodu düzeltildikten sonra yeni mesajlar doğru dizinlere düşmeye başladı.
Ardından yanlış konumdaki:
769
mesaj güvenli şekilde doğru dizinlere taşındı.
Kuyruk:
767
mesajdan:
35
mesaja düştü.
Kalan mesajların ise bir kısmı normal SMTP sebepleriyle bekleyen veya frozen durumdaki mesajlardı.
Teknik Özet
Problem:
MailScanner yanlış split spool klasörüne yazıyor.
Yanlış:
input/0/
Doğru örnek:
input/J/
Eski MailScanner:
substr($name,-13,1)
substr($name,-20,1)
Geçici fix:
substr($name,5,1)
Daha sağlam fix:
my ($firstpart) = split(/-/, $name, 2);
substr($firstpart, -1, 1)
Sonuç:
Yeni mailler doğru spool klasörüne yazıldı.
Eski 769 mesaj doğru dizinlere taşındı.
Mail kuyruğu büyük ölçüde temizlendi.
Yıldız Bilişim Hizmetleri - Sistem Yönetimi Teknik Notu
Bu çözüm gerçek bir cPanel, Exim ve MailScanner üretim sunucusunda teşhis edilmiş; spool dosyaları, servis logları ve gerçek e-posta teslimleri ile doğrulanmıştır.
Uyarı: Bu işlemler doğrudan Exim spool dosyaları ve MailScanner Perl kaynak kodları üzerinde değişiklik yapmaktadır. Üretim sunucusunda işlem yapmadan önce mutlaka yedek alın. Mail kuyruğundaki dosyaları bilinçsiz şekilde silmeyin veya taşımayın.
Yıldız Bilişim Otomatik Exim 4.100 / MailScanner Split Spool Koruma Sistemi
Manuel olarak uygulanan split spool düzeltmesinin gelecekte yapılacak MailScanner veya cPanel güncellemeleri sırasında kaybolmaması için ayrıca otomatik bir koruma sistemi oluşturduk.
Bu sistemin amacı yalnızca mevcut sorunu çözmek değil, gelecekte MailScanner kaynak dosyaları yeniden eski hale dönerse problemi otomatik olarak tespit edip tekrar düzeltmektir.
Sistem iki parçadan oluşur:
- Split spool compatibility / patch scripti
- Yıldız MailScanner Update sistemi içerisine otomatik kontrol entegrasyonu
Buna ek olarak her güncellemenin sonunda gerçek mail kuyruğu taranarak yanlış split spool dizininde mesaj bulunup bulunmadığı da otomatik kontrol edilir.
1. Split Spool Otomatik Patch Scripti
Öncelikle aşağıdaki dosyayı oluşturuyoruz:
/usr/local/sbin/yildiz-msfe-splitspool-fix
Dosyayı oluşturmak için:
nano /usr/local/sbin/yildiz-msfe-splitspool-fix
İçeriği:
#!/bin/bash
set -u
LOG="/var/log/yildiz-msfe-update.log"
BASE="/usr/mailscanner/usr/share/MailScanner/perl/MailScanner"
FILES=(
"$BASE/EximDiskStore.pm"
"$BASE/EximDiskStoreNew.pm"
)
STAMP="$(date '+%Y%m%d-%H%M%S')"
log() {
echo "$*" | tee -a "$LOG"
}
log "[SplitSpool] Compatibility check started"
CHANGED=0
ERRORS=0
for FILE in "${FILES[@]}"; do
if [ ! -f "$FILE" ]; then
log "[SplitSpool] ERROR: file not found: $FILE"
ERRORS=$((ERRORS+1))
continue
fi
# Dinamik fix zaten varsa hiçbir değişiklik yapma
if grep -Fq 'substr($firstpart,-1,1)' "$FILE" || \
grep -Fq 'substr($firstpart, -1, 1)' "$FILE"; then
log "[SplitSpool] OK: already patched: $FILE"
continue
fi
# Sadece bildiğimiz eski veya geçici kalıplara müdahale et
if ! grep -Eq "return '/' \. substr\\(\\\$name,(-13|-20|5),1\\) \. '/';" "$FILE"; then
log "[SplitSpool] WARNING: unknown OutQDir format, not modifying: $FILE"
ERRORS=$((ERRORS+1))
continue
fi
BACKUP="${FILE}.pre-splitspool-${STAMP}"
if ! cp -a "$FILE" "$BACKUP"; then
log "[SplitSpool] ERROR: backup failed: $FILE"
ERRORS=$((ERRORS+1))
continue
fi
log "[SplitSpool] Backup: $BACKUP"
sed -i \
-e "s@return '/' \. substr(\\\$name,-13,1) \. '/';@my (\\\$firstpart) = split(/-/, \\\$name, 2);\\n return '/' . substr(\\\$firstpart,-1,1) . '/';@" \
-e "s@return '/' \. substr(\\\$name,-20,1) \. '/';@my (\\\$firstpart) = split(/-/, \\\$name, 2);\\n return '/' . substr(\\\$firstpart,-1,1) . '/';@" \
-e "s@return '/' \. substr(\\\$name,5,1) \. '/';@my (\\\$firstpart) = split(/-/, \\\$name, 2);\\n return '/' . substr(\\\$firstpart,-1,1) . '/';@" \
"$FILE"
if grep -Fq 'substr($firstpart,-1,1)' "$FILE"; then
log "[SplitSpool] PATCHED: $FILE"
CHANGED=$((CHANGED+1))
else
log "[SplitSpool] ERROR: verification failed, restoring: $FILE"
cp -a "$BACKUP" "$FILE"
ERRORS=$((ERRORS+1))
fi
done
log "[SplitSpool] Changed=$CHANGED Errors=$ERRORS"
if [ "$ERRORS" -gt 0 ]; then
log "[SplitSpool] RESULT=ERROR"
exit 1
fi
if [ "$CHANGED" -gt 0 ]; then
log "[SplitSpool] RESULT=PATCHED"
exit 10
fi
log "[SplitSpool] RESULT=OK"
exit 0
Script Yetkileri
chown root:root /usr/local/sbin/yildiz-msfe-splitspool-fix
chmod 700 /usr/local/sbin/yildiz-msfe-splitspool-fix
Shell syntax kontrolü:
bash -n /usr/local/sbin/yildiz-msfe-splitspool-fix && echo "SYNTAX OK"
Beklenen:
SYNTAX OK
Script Nasıl Çalışır?
Script iki MailScanner dosyasını kontrol eder:
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStore.pm
/usr/mailscanner/usr/share/MailScanner/perl/MailScanner/EximDiskStoreNew.pm
Eğer aşağıdaki dinamik yapı zaten mevcutsa dosyalara dokunmaz:
my ($firstpart) = split(/-/, $name, 2);
return '/' . substr($firstpart,-1,1) . '/';
Bu durumda log:
[SplitSpool] OK: already patched
[SplitSpool] Changed=0 Errors=0
[SplitSpool] RESULT=OK
şeklinde olur.
Eğer MailScanner güncellemesi eski kodu geri getirmişse:
substr($name,-13,1)
veya:
substr($name,-20,1)
ya da daha önce kullanılan geçici:
substr($name,5,1)
kalıplarından biri tespit edilir.
Script önce otomatik yedek alır:
EximDiskStore.pm.pre-splitspool-YYYYMMDD-HHMMSS
ardından kodu şu dinamik yapıya dönüştürür:
my ($firstpart) = split(/-/, $name, 2);
return '/' . substr($firstpart,-1,1) . '/';
Özel Exit Code Kullanımı
Script üç farklı sonuç kodu döndürür:
0 = Patch zaten doğru, değişiklik gerekmedi
10 = Patch uygulandı
1 = Hata veya bilinmeyen kod yapısı
Buradaki 10 bir hata değildir. Özellikle patch uygulanmış olduğunu ana updater scriptine bildirmek için kullanılır.
2. Yıldız MailScanner Update Sistemine Otomatik Entegrasyon
Bizim ana updater dosyamız:
/usr/local/sbin/yildiz-msfe-update
Bu dosya ClamAV, MailScanner listeleri, SpamAssassin ve Exim restart işlemlerini gerçekleştirir.
Split spool kontrolü update sistemine aşağıdaki bölüm ile eklenmiştir:
echo "[4] Exim / MailScanner SplitSpool compatibility" >> "$LOG"
if [ -x /usr/local/sbin/yildiz-msfe-splitspool-fix ]; then
/usr/local/sbin/yildiz-msfe-splitspool-fix >/dev/null 2>&1
SPLIT_RC=$?
if [ "$SPLIT_RC" -eq 0 ]; then
echo "splitspool check exit=0 (OK)" >> "$LOG"
elif [ "$SPLIT_RC" -eq 10 ]; then
echo "splitspool check exit=10 (PATCHED)" >> "$LOG"
systemctl restart MailScanner >> "$LOG" 2>&1
echo "MailScanner restart after patch exit=$?" >> "$LOG"
else
echo "splitspool check exit=$SPLIT_RC (ERROR)" >> "$LOG"
fi
else
echo "splitspool fix script not found" >> "$LOG"
fi
Neden MailScanner Sadece Patch Uygulandığında Restart Ediliyor?
Patch zaten mevcutsa gereksiz yere MailScanner restart edilmez.
Ancak script:
exit 10
döndürürse bu, MailScanner Perl kaynak dosyasının değiştirildiği anlamına gelir.
Çalışan MailScanner proseslerinin yeni kodu yükleyebilmesi için:
systemctl restart MailScanner
otomatik çalıştırılır.
3. Exim Restart
Daha sonra normal update akışı devam eder:
echo "[5] Restart services" >> "$LOG"
echo "spamd disabled or not managed here, skipped" >> "$LOG"
/scripts/restartsrv_exim >> "$LOG" 2>&1
echo "restartsrv_exim exit=$?" >> "$LOG"
4. Güncelleme Sonrası Otomatik Split Spool Sağlık Kontrolü
Sadece patch dosyasının mevcut olması yeterli değildir.
Güncellemeden sonra gerçek mail kuyruğu da taranmalıdır.
Bu nedenle updater'ın sonunda aşağıdaki kontrol çalıştırılır:
echo "[6] SplitSpool health check" >> "$LOG"
WRONG=0
while IFS= read -r HFILE; do
ID=$(basename "$HFILE" -H)
ACTUAL=$(basename "$(dirname "$HFILE")")
FIRSTPART="${ID%%-*}"
EXPECTED="${FIRSTPART: -1}"
if [ "$ACTUAL" != "$EXPECTED" ]; then
WRONG=$((WRONG+1))
fi
done < <(
find /var/spool/exim/input \
-type f \
-name '*-H' \
2>/dev/null
)
echo "splitspool wrong messages=$WRONG" >> "$LOG"
if [ "$WRONG" -eq 0 ]; then
echo "splitspool health=OK" >> "$LOG"
else
echo "splitspool health=WARNING" >> "$LOG"
fi
echo "MailScanner status=$(systemctl is-active MailScanner 2>/dev/null)" >> "$LOG"
OUTCOUNT=$(/usr/sbin/exim -C /etc/exim_outgoing.conf -bpc 2>/dev/null)
echo "outgoing queue=$OUTCOUNT" >> "$LOG"
Sağlıklı Güncelleme Sonrası Beklenen Log
Başarılı bir güncelleme sonunda log aşağıdaki yapıda olmalıdır:
[4] Exim / MailScanner SplitSpool compatibility
[SplitSpool] Compatibility check started
[SplitSpool] OK: already patched:
EximDiskStore.pm
[SplitSpool] OK: already patched:
EximDiskStoreNew.pm
[SplitSpool] Changed=0 Errors=0
[SplitSpool] RESULT=OK
splitspool check exit=0 (OK)
[5] Restart services
restartsrv_exim exit=0
[6] SplitSpool health check
splitspool wrong messages=0
splitspool health=OK
MailScanner status=active
outgoing queue=35
Yildiz MSFE update finished
En Önemli Sağlık Satırları
Aşağıdaki dört satır sağlıklı sistemi özetler:
splitspool check exit=0 (OK)
splitspool wrong messages=0
splitspool health=OK
MailScanner status=active
Özellikle:
splitspool wrong messages=0
değeri, Exim spool mesajlarının yanlış alt dizinlere düşmediğini gösterir.
MailScanner Güncellemesi Patch'i Silse Ne Olur?
Örneğin gelecekte MailScanner güncellemesi aşağıdaki eski satırı geri getirirse:
return '/' . substr($name,-13,1) . '/';
veya:
return '/' . substr($name,-20,1) . '/';
Yıldız MailScanner Update sistemi bunu otomatik algılar.
Log:
[SplitSpool] PATCHED:
EximDiskStore.pm
[SplitSpool] PATCHED:
EximDiskStoreNew.pm
[SplitSpool] Changed=2 Errors=0
[SplitSpool] RESULT=PATCHED
splitspool check exit=10 (PATCHED)
şeklinde görünür.
Daha sonra sistem otomatik olarak:
systemctl restart MailScanner
çalıştırır.
Böylece yeni kod hemen aktif hale gelir.
Bilinmeyen MailScanner Kod Yapısı Tespit Edilirse
Script bilinmeyen bir kaynak kod yapısı gördüğünde körlemesine değişiklik yapmaz.
Örneğin:
[SplitSpool] WARNING: unknown OutQDir format, not modifying
ve:
[SplitSpool] RESULT=ERROR
üretir.
Bu özellikle önemlidir. MailScanner gelecekte OutQDir fonksiyonunu tamamen değiştirirse eski patch mantığının yeni kaynak kodunu bozmasının önüne geçilmiş olur.
Force MailScanner Update Butonu ile Kullanım
WHM MailScanner Front-End içerisinde kullanılan:
<input
type="submit"
class="btn btn-default"
value="Force MailScanner Update"
>
butonuna basıldığında artık yalnızca phishing, spam ve antivirüs listeleri güncellenmez.
Sistem aynı zamanda:
- Exim / MailScanner uyumluluğunu kontrol eder.
- Split spool patch kaybolmuşsa otomatik uygular.
- Gerekirse MailScanner'ı restart eder.
- Exim'i restart eder.
- Gerçek outgoing mail kuyruğunu kontrol eder.
- Yanlış split spool dizinindeki mesajları sayar.
- MailScanner servis durumunu loglar.
- Sonucu OK, PATCHED veya WARNING olarak kayıt altına alır.
Manuel Kontrol
Updater manuel olarak da çalıştırılabilir:
/usr/local/sbin/yildiz-msfe-update
Son log kayıtları:
tail -70 /var/log/yildiz-msfe-update.log
Kontrol edilmesi gereken en önemli bölüm:
[6] SplitSpool health check
splitspool wrong messages=0
splitspool health=OK
MailScanner status=active
Neden Bu Yapı Daha Güvenli?
Klasik bir otomatik patch sistemi her güncellemede dosyanın içine körlemesine müdahale edebilir.
Buradaki yapı ise önce mevcut kodu kontrol eder.
- Patch mevcutsa hiçbir değişiklik yapmaz.
- Bilinen eski yapı varsa önce yedek alır ve sonra patch uygular.
- Patch sonrası sonucu doğrular.
- Doğrulama başarısızsa otomatik olarak yedeği geri yükler.
- Bilinmeyen kod yapısında hiçbir değişiklik yapmaz.
Dolayısıyla sistem idempotent çalışır.
Aynı script defalarca çalıştırılsa bile doğru dosyalara tekrar tekrar müdahale etmez.
Canlı Sunucuda Doğrulanan Sonuç
Kurulum tamamlandıktan sonra updater gerçek üretim sunucusunda tekrar çalıştırıldı.
Son sağlık kontrolü:
splitspool check exit=0 (OK)
splitspool wrong messages=0
splitspool health=OK
MailScanner status=active
outgoing queue=35
şeklinde sonuçlandı.
Bu durum:
- MailScanner patch'inin aktif olduğunu,
- Exim 4.100 ile doğru split spool klasörlerinin kullanıldığını,
- Yanlış dizinde mail bulunmadığını,
- MailScanner servisinin aktif olduğunu,
- Outgoing queue runner'ın çalıştığını
doğrulamaktadır.
Önemli: Otomatik patch sistemi gelecekte yapılacak tüm MailScanner veya Exim değişikliklerini garanti altında tutmaz. MailScanner kaynak kod yapısı tamamen değişirse script bilinmeyen yapıyı tespit ederek müdahale etmeyecek ve logda hata/uyarı oluşturacaktır. Bu davranış kasıtlıdır ve üretim sunucusunda yanlış otomatik değişiklik yapılmasını önlemek amacıyla tasarlanmıştır.
Yıldız Bilişim Hizmetleri - Otomatik MailScanner / Exim Uyumluluk Sistemi
Bu yapı gerçek cPanel, Exim 4.100 ve MailScanner 5.4.4 üretim ortamında uygulanmış ve güncelleme sonrası otomatik sağlık kontrolü ile doğrulanmıştır.

Henüz onaylanmış yorum yok. İlk yorumu sen bırakabilirsin.
Yorum Yaz
Yorum yayınlanmadan önce e-posta adresiniz 120 saniyelik kodla doğrulanır ve yorum yönetici onayına gönderilir.