Отстраняване на грешки при внедряване на софтуер със strace

Отстраняване на грешки при внедряване на софтуер със strace

Ежедневната ми работа е предимно внедряване на софтуер, което означава, че прекарвам много време в опити да отговоря на въпроси като:

  • Този софтуер работи за разработчика, но не и за мен. Защо?
  • Вчера този софтуер работи за мен, но днес не работи. Защо?

Това е вид отстраняване на грешки, което е малко по-различно от обикновеното отстраняване на грешки в софтуера. Редовното отстраняване на грешки е свързано с логиката на кода, но отстраняването на грешки при внедряване е свързано с взаимодействието между кода и средата. Дори ако коренът на проблема е логическа грешка, фактът, че всичко работи на една машина, а не на друга, означава, че проблемът по някакъв начин е в средата.

Така че вместо обичайните инструменти за отстраняване на грешки като GDB Имам различен набор от инструменти за отстраняване на грешки при разполагане. И моят любим инструмент за справяне с проблема като „Защо този софтуер не работи за мен?“ Наречен Strace.

Какво е strace?

Strace е инструмент за „проследяване на системни повиквания“. Първоначално е създаден за Linux, но същите трикове за отстраняване на грешки могат да бъдат направени с инструменти за други системи (DTrace или ktration).

Основното приложение е много просто. Просто трябва да стартирате strace с която и да е команда и тя ще изхвърли всички системни повиквания (въпреки че първо вероятно ще трябва да я инсталирате сами Strace):

$ strace echo Hello
...Snip lots of stuff...
write(1, "Hellon", 6)                  = 6
close(1)                                = 0
close(2)                                = 0
exit_group(0)                           = ?
+++ exited with 0 +++

Какви са тези системни повиквания? Това е нещо като API за ядрото на операционната система. Някога софтуерът имаше директен достъп до хардуера, на който работеше. Ако, например, трябваше да покаже нещо на екрана, той играеше с портове или карти с памет регистри за видео устройства. Когато многозадачните компютърни системи станаха популярни, цареше хаос, тъй като различни приложения се бореха за хардуера. Грешки в едно приложение могат да свалят други, ако не и цялата система. След това в процесора се появиха режими на привилегии (или „защита на звънене“). Ядрото стана най-привилегированото: получи пълен достъп до хардуера, създавайки по-малко привилегировани приложения, които вече трябваше да поискат достъп от ядрото, за да взаимодействат с хардуера чрез системни повиквания.

На двоично ниво системното извикване е малко по-различно от обикновеното извикване на функция, но повечето програми използват обвивка в стандартната библиотека. Тези. стандартната библиотека POSIX C съдържа извикване на функция напиши (), който съдържа целия специфичен за архитектурата код за системното извикване пиша.

Отстраняване на грешки при внедряване на софтуер със strace

Накратко, всяко взаимодействие между приложение и неговата среда (компютърни системи) се осъществява чрез системни повиквания. Следователно, когато софтуерът работи на една машина, но не и на друга, би било добре да погледнете резултатите от проследяването на системните повиквания. По-конкретно, ето списък с типични точки, които могат да бъдат анализирани с помощта на проследяване на системно повикване:

  • Конзолен I/O
  • Мрежови I/O
  • Достъп до файловата система и файлов I/O
  • Управление на живота на процесна нишка
  • Управление на паметта на ниско ниво
  • Достъп до специфични драйвери на устройства

Кога да използвам strace?

На теория, Strace използва се с всяка програма в потребителското пространство, защото всяка програма в потребителското пространство трябва да прави системни извиквания. Работи по-ефективно с компилирани програми на ниско ниво, но работи и с езици на високо ниво като Python, ако можете да пресечете допълнителния шум от времето за изпълнение и интерпретатора.

В целия си блясък Strace се проявява по време на отстраняване на грешки на софтуер, който работи добре на една машина, но внезапно спира да работи на друга, извеждайки неясни съобщения за файлове, разрешения или неуспешни опити за изпълнение на някакви команди или нещо друго... Жалко, но не става се комбинират толкова добре с проблеми на високо ниво, като например грешки при проверка на сертификат. Обикновено това изисква комбинация Straceпонякога ltrace и инструменти от по-високо ниво (като инструмента за команден ред OpenSSL за отстраняване на грешки в сертификата).

Ще използваме самостоятелен сървър като пример, но проследяването на системни повиквания често може да се извърши на по-сложни платформи за внедряване. Просто трябва да изберете правилните инструменти.

Прост пример за отстраняване на грешки

Да приемем, че искате да стартирате невероятното сървърно приложение foo и ето какво в крайна сметка получавате:

$ foo
Error opening configuration file: No such file or directory

Очевидно не може да намери конфигурационния файл, който сте написали. Това се случва, защото понякога, когато мениджърите на пакети компилират приложение, те заместват очакваните файлови местоположения. И ако следвате ръководството за инсталиране на една дистрибуция, в друга ще намерите файлове, напълно различни от това, където сте очаквали. Проблемът може да бъде решен за няколко секунди, ако съобщението за грешка казва къде да търсите конфигурационния файл, но не го прави. И така, къде да търсим?

Ако имате достъп до изходния код, можете да го прочетете и да разберете всичко. Добър резервен план, но не и най-бързото решение. Можете да прибегнете до програма за отстраняване на грешки стъпка по стъпка като GDB и вижте какво прави програмата, но е много по-ефективно да използвате инструмент, който е специално проектиран да показва взаимодействие с околната среда: Strace.

Продукция Strace може да изглежда излишно, но добрата новина е, че повечето от тях могат безопасно да бъдат игнорирани. Често е полезно да използвате оператора -o, за да запазите резултатите от проследяването в отделен файл:

$ strace -o /tmp/trace foo
Error opening configuration file: No such file or directory
$ cat /tmp/trace
execve("foo", ["foo"], 0x7ffce98dc010 /* 16 vars */) = 0
brk(NULL)                               = 0x56363b3fb000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=25186, ...}) = 0
mmap(NULL, 25186, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f2f12cf1000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "177ELF2113 3 > 1 260A2 "..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1824496, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f2f12cef000
mmap(NULL, 1837056, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f2f12b2e000
mprotect(0x7f2f12b50000, 1658880, PROT_NONE) = 0
mmap(0x7f2f12b50000, 1343488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x22000) = 0x7f2f12b50000
mmap(0x7f2f12c98000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x16a000) = 0x7f2f12c98000
mmap(0x7f2f12ce5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b6000) = 0x7f2f12ce5000
mmap(0x7f2f12ceb000, 14336, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f2f12ceb000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7f2f12cf0500) = 0
mprotect(0x7f2f12ce5000, 16384, PROT_READ) = 0
mprotect(0x56363b08b000, 4096, PROT_READ) = 0
mprotect(0x7f2f12d1f000, 4096, PROT_READ) = 0
munmap(0x7f2f12cf1000, 25186)           = 0
openat(AT_FDCWD, "/etc/foo/config.json", O_RDONLY) = -1 ENOENT (No such file or directory)
dup(2)                                  = 3
fcntl(3, F_GETFL)                       = 0x2 (flags O_RDWR)
brk(NULL)                               = 0x56363b3fb000
brk(0x56363b41c000)                     = 0x56363b41c000
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x8), ...}) = 0
write(3, "Error opening configuration file"..., 60) = 60
close(3)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

Приблизително цялата първа страница на изхода Strace - Обикновено това е подготовка на ниско ниво за изстрелване. (Много обаждания mmap, mprotect, перо за неща като откриване на памет на ниско ниво и показване на динамични библиотеки.) Всъщност по време на отстраняване на грешки изходът Strace По-добре е да четете от самия край. По-долу ще има предизвикателство пиша, което показва съобщение за грешка. Поглеждаме отгоре и виждаме първото погрешно системно повикване - повикването openat, което извежда грешка ENOENT („файлът или директорията не са намерени“) се опитва да отвори /etc/foo/config.json. Тук трябва да е конфигурационният файл.

Това беше само пример, но бих казал, че 90% от времето, което използвам Strace, няма нищо много по-трудно за правене от това. По-долу е пълно ръководство за отстраняване на грешки стъпка по стъпка:

  • Разстройте се поради неясно съобщение за системна грешка от програма
  • Рестартирайте програмата с Strace
  • Намерете съобщението за грешка в резултатите от проследяването
  • Отидете по-високо, докато достигнете първото неуспешно системно повикване

Много е вероятно системното повикване в стъпка 4 да разкрие какво се е объркало.

съвети

Преди да ви покажа пример за по-сложно отстраняване на грешки, ще ви покажа няколко трика за ефективна употреба Strace:

човекът е твой приятел

На много *nix системи, пълен списък на системните извиквания към ядрото може да бъде получен чрез стартиране системни повиквания на човека. Ще видите неща като brk (2), което означава, че може да се получи повече информация чрез стартиране мъж 2 бр.

Малък рейк: човек 2 вилица показва ми страницата за черупката вилица () в GNU libc, което се оказва, че се реализира чрез извикване клонинг(). Семантика на повикването вилица остава същата, ако напишете програма, използваща вилица ()и стартирайте проследяване - няма да намеря обаждания вилица, вместо тях ще има клонинг(). Такива рейкове само ви объркват, ако започнете да сравнявате източника с изхода Strace.

Използвайте -o, за да запишете изхода във файл

Strace може да генерира обширен изход, така че често е полезно да съхранявате резултатите от проследяването в отделни файлове (както в примера по-горе). Това също помага да се избегне объркването на изхода на програмата с изхода Strace в конзолата.

Използвайте -s, за да видите повече данни за аргументи

Може да сте забелязали, че втората половина на съобщението за грешка не е показана в примерната трасировка по-горе. Това е защото Strace по подразбиране показва само първите 32 байта от низовия аргумент. Ако искате да видите повече, добавете нещо подобно -s 128 към обаждането Strace.

-y улеснява проследяването на файлове, сокети и т.н.

„Всичко е файл“ означава, че *nix системите извършват всички I/O, използвайки файлови дескриптори, независимо дали това се отнася за файл, мрежа или междупроцесни канали. Това е удобно за програмиране, но затруднява проследяването на това, което наистина се случва, когато видите общи чета и пиша в резултатите от проследяването на системните повиквания.

Чрез добавяне на оператор ш, ще принудите Strace анотирайте всеки файлов дескриптор в изхода с бележка към какво сочи.

Прикрепете към вече работещ процес с -p**

Както ще видите от примера по-долу, понякога трябва да проследите програма, която вече работи. Ако е известно, че се изпълнява като процес 1337 (да речем, от изхода ps), тогава можете да го проследите по следния начин:

$ strace -p 1337
...system call trace output...

Може да имате нужда от root права.

Използвайте -f за наблюдение на дъщерни процеси

Strace По подразбиране проследява само един процес. Ако този процес ражда дъщерни процеси, тогава може да се види системното извикване за раждане на дъщерния процес, но системните извиквания на дъщерния процес няма да бъдат показани.

Ако смятате, че грешката е в дъщерен процес, използвайте оператора -f, това ще позволи неговото проследяване. Недостатъкът на това е, че резултатът ще ви обърка още повече. Кога Strace проследява един процес или една нишка, показва единичен поток от събития на повикване. Когато проследява няколко процеса наведнъж, може да видите началото на повикване, прекъснато от съобщение , след това - куп извиквания за други клонове за изпълнение и едва след това - края на първия <…foocall resumed>. Или разделете всички резултати от трасирането в различни файлове, като също използвате оператора -фф (подробности в ръководство на Strace).

Филтрирайте следите с помощта на -e

Както можете да видите, резултатът от проследяването е истинска купчина от всички възможни системни повиквания. Флаг -e Можете да филтрирате следата (вижте ръководство на Strace). Основното предимство е, че е по-бързо да стартирате филтрирано проследяване, отколкото да направите пълно проследяване и след това Впиши`в. Честно казано, почти винаги не ми пука.

Не всички грешки са лоши

Прост и често срещан пример е програма, която търси файл на няколко места едновременно, като черупка, която търси директория, която съдържа изпълним файл:

$ strace sh -c uname
...
stat("/home/user/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/uname", 0x7ffceb817820) = -1 ENOENT (No such file or directory)
stat("/usr/bin/uname", {st_mode=S_IFREG|0755, st_size=39584, ...}) = 0
...

Евристиките като „последна неуспешна заявка преди докладване за грешка“ са добри при намирането на подходящи грешки. Както и да е, логично е да започнем от самия край.

Уроците по програмиране на C могат да ви помогнат да разберете системните повиквания.

Стандартните извиквания към C библиотеки не са системни извиквания, а само тънък повърхностен слой. Така че, ако разбирате поне малко как и какво да правите в C, ще ви бъде по-лесно да разберете резултатите от проследяването на системното извикване. Например, имате проблеми с отстраняването на грешки при повиквания към мрежови системи, погледнете същата класика Ръководство на Bija за мрежово програмиране.

По-сложен пример за отстраняване на грешки

Вече казах, че примерът за просто отстраняване на грешки е пример за това, с което най-често трябва да се справям, когато работя Strace. Понякога обаче е необходимо истинско разследване, така че ето пример от реалния живот за по-разширено отстраняване на грешки.

bcron - планировчик за обработка на задачи, друга реализация на *nix демон Cron. Той е инсталиран на сървъра, но когато някой се опита да редактира графика, се случва следното:

# crontab -e -u logs
bcrontab: Fatal: Could not create temporary file

Добре, това означава bcron се опита да напише определен файл, но не се получи и той не иска да признае защо. Разкриване Strace:

# strace -o /tmp/trace crontab -e -u logs
bcrontab: Fatal: Could not create temporary file
# cat /tmp/trace
...
openat(AT_FDCWD, "bcrontab.14779.1573691864.847933", O_RDONLY) = 3
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000
read(3, "#Ansible: logsaggn20 14 * * * lo"..., 8192) = 150
read(3, "", 8192)                       = 0
munmap(0x7f82049b4000, 8192)            = 0
close(3)                                = 0
socket(AF_UNIX, SOCK_STREAM, 0)         = 3
connect(3, {sa_family=AF_UNIX, sun_path="/var/run/bcron-spool"}, 110) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82049b4000
write(3, "156:Slogs #Ansible: logsaggn20 1"..., 161) = 161
read(3, "32:ZCould not create temporary f"..., 8192) = 36
munmap(0x7f82049b4000, 8192)            = 0
close(3)                                = 0
write(2, "bcrontab: Fatal: Could not creat"..., 49) = 49
unlink("bcrontab.14779.1573691864.847933") = 0
exit_group(111)                         = ?
+++ exited with 111 +++

В самия край има съобщение за грешка пиша, но този път нещо е различно. Първо, няма съответна грешка в системното извикване, която обикновено се появява преди това. Второ, ясно е, че някъде някой вече е прочел съобщението за грешка. Изглежда, че истинският проблем е някъде другаде и bcrontab просто възпроизвежда съобщението.

Ако погледнете човек 2 чете, можете да видите, че първият аргумент (3) е файлов дескриптор, който *nix използва за цялата I/O обработка. Как да разбера какво представлява файловият дескриптор 3? В този конкретен случай можете да бягате Strace с оператор ш (вижте по-горе) и автоматично ще ви каже, но за да разберете неща като тези, е полезно да знаете как да четете и анализирате резултатите от проследяването.

Източникът на файлов дескриптор може да бъде едно от много системни извиквания (всичко зависи от това за какво служи дескрипторът - конзола, мрежов сокет, самият файл или нещо друго), но както и да е, ние търсим извиквания чрез връщане на 3 (т.е. търсим „= 3“ в резултатите от проследяването). В този резултат има 2 от тях: openat на самия връх и гнездо По средата. openat отваря файла, но близо(3) след това ще покаже, че се затваря отново. (Rake: файловите дескриптори могат да се използват повторно, когато се отварят и затварят). Обадете се гнездо () подходящ, защото е последният преди Прочети(), и се оказва, че bcrontab работи с нещо през сокет. Следващият ред показва, че файловият дескриптор е свързан с unix домейн сокет по пътя /var/run/bcron-spool.

И така, трябва да намерим процеса, свързан с unix сокет от друга страна. Има няколко хитри трика за тази цел, като и двата са полезни за отстраняване на грешки при внедряване на сървъри. Първият е да използвате NETSTAT или по-нова ss (състояние на сокета). И двете команди показват активните мрежови връзки на системата и приемат изявлението -l за описание на гнездата за слушане, както и на оператора -p за показване на програми, свързани към сокета като клиент. (Има още много полезни опции, но тези две са достатъчни за тази задача.)

# ss -pl | grep /var/run/bcron-spool
u_str LISTEN 0   128   /var/run/bcron-spool 1466637   * 0   users:(("unixserver",pid=20629,fd=3))

Това предполага, че слушателят е командата inixserver, изпълняващ се с идентификатор на процес 20629. (И, случайно, той използва файлов дескриптор 3 като сокет.)

Вторият наистина полезен инструмент за намиране на същата информация се нарича също. Той изброява всички отворени файлове (или файлови дескриптори) в системата. Или можете да получите информация за един конкретен файл:

# lsof /var/run/bcron-spool
COMMAND   PID   USER  FD  TYPE  DEVICE              SIZE/OFF  NODE    NAME
unixserve 20629 cron  3u  unix  0x000000005ac4bd83  0t0       1466637 /var/run/bcron-spool type=STREAM

Процесът 20629 е дълготраен сървър, така че можете да го прикачите Strace използвайки нещо подобно strace -o /tmp/trace -p 20629. Ако редактирате задание на cron в друг терминал, ще получите изход за проследяване с грешка. И ето го резултата:

accept(3, NULL, NULL)                   = 4
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21181
close(4)                                = 0
accept(3, NULL, NULL)                   = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21181, si_uid=998, si_status=0, si_utime=0, si_stime=0} ---
wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], WNOHANG|WSTOPPED, NULL) = 21181
wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (No child processes)
rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0
rt_sigreturn({mask=[]})                 = 43
accept(3, NULL, NULL)                   = 4
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21200
close(4)                                = 0
accept(3, NULL, NULL)                   = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=21200, si_uid=998, si_status=111, si_utime=0, si_stime=0} ---
wait4(0, [{WIFEXITED(s) && WEXITSTATUS(s) == 111}], WNOHANG|WSTOPPED, NULL) = 21200
wait4(0, 0x7ffe6bc36764, WNOHANG|WSTOPPED, NULL) = -1 ECHILD (No child processes)
rt_sigaction(SIGCHLD, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, {sa_handler=0x55d244bdb690, sa_mask=[CHLD], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x7faa47ab9840}, 8) = 0
rt_sigreturn({mask=[]})                 = 43
accept(3, NULL, NULL

(Последно приемете () няма да бъде завършен при проследяване.) Отново, за съжаление, този резултат не съдържа грешката, която търсим. Не виждаме никакви съобщения, които bcrontag изпраща или получава от сокета. Вместо това пълен контрол на процеса (клонинг, изчакай4, SIGCHLD и т.н.) Този процес поражда дъщерен процес, който, както можете да предположите, върши истинската работа. И ако трябва да хванете следите й, добавете към обаждането strace -f. Това ще открием, когато търсим съобщението за грешка в новия резултат със strace -f -o /tmp/trace -p 20629:

21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied) 
21470 write(1, "32:ZCould not create temporary f"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)
21470 exit_group(111)                   = ?
21470 +++ exited with 111 +++

Сега, това е нещо. Процес 21470 получава грешка „достъпът е отказан“, когато се опитва да създаде файл на път tmp/spool.21470.1573692319.854640 (свързани с текущата работна директория). Ако знаехме само текущата работна директория, щяхме да знаем и пълния път и да можем да разберем защо процесът не може да създаде своя временен файл в нея. За съжаление, процесът вече е излязъл, така че не можете просто да използвате lsof -p 21470 за да намерите текущата директория, но можете да работите в обратна посока - потърсете PID 21470 системни извиквания, които променят директорията. (Ако няма такива, PID 21470 трябва да ги е наследил от своя родител и това вече е чрез lsof -p не може да се открие.) Това системно повикване е CHDIR (което лесно се установява с помощта на съвременните онлайн търсачки). И ето резултатът от обратни търсения въз основа на резултатите от проследяването, чак до сървър PID 20629:

20629 clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7faa47c44810) = 21470
...
21470 execve("/usr/sbin/bcron-spool", ["bcron-spool"], 0x55d2460807e0 /* 27 vars */) = 0
...
21470 chdir("/var/spool/cron")          = 0
...
21470 openat(AT_FDCWD, "tmp/spool.21470.1573692319.854640", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied) 
21470 write(1, "32:ZCould not create temporary f"..., 36) = 36
21470 write(2, "bcron-spool[21470]: Fatal: logs:"..., 84) = 84
21470 unlink("tmp/spool.21470.1573692319.854640") = -1 ENOENT (No such file or directory)
21470 exit_group(111)                   = ?
21470 +++ exited with 111 +++

(Ако сте се изгубили, може да искате да прочетете предишната ми публикация за *nix управление на процеси и черупки.) И така, сървърът PID 20629 не получи разрешение да създаде файл на пътя /var/spool/cron/tmp/spool.21470.1573692319.854640. Най-вероятно причината за това са класическите настройки за разрешения на файловата система. Да проверим:

# ls -ld /var/spool/cron/tmp/
drwxr-xr-x 2 root root 4096 Nov  6 05:33 /var/spool/cron/tmp/
# ps u -p 20629
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
cron     20629  0.0  0.0   2276   752 ?        Ss   Nov14   0:00 unixserver -U /var/run/bcron-spool -- bcron-spool

Ето къде е заровено кучето! Сървърът работи като потребител cron, но само root има разрешение да пише в директорията /var/spool/cron/tmp/. Проста команда chown cron /var/spool/cron/tmp/ ще принуди bcron работят правилно. (Ако това не е проблемът, тогава следващият най-вероятен заподозрян е модул за сигурност на ядрото като SELinux или AppArmor, така че бих проверил регистъра на съобщенията на ядрото с dmesg.)

Общо

Проследяването на системни повиквания може да бъде непосилно за начинаещ, но се надявам, че съм показал, че те са бърз начин за отстраняване на грешки в цял клас често срещани проблеми с внедряването. Представете си, че се опитвате да дебъгвате мултипроцес bcronс помощта на програма за отстраняване на грешки стъпка по стъпка.

Разборът на резултатите от проследяването назад по веригата на системните повиквания изисква умения, но както казах, почти винаги, използвайки Strace, просто получавам резултата от трасирането и търся грешки, започвайки от края. Така или иначе, Strace ми помага да спестя много време за отстраняване на грешки. Надявам се и на вас да ви е от полза.

Източник: www.habr.com

Добавяне на нов коментар