Обнаружение ошибки с помощью Perl system () с несколькими конвейерными процессами - PullRequest
0 голосов
/ 07 января 2019

Я пытаюсь осуществить восстановление базы данных MySQL из файла, зашифрованного GPG.

Отлично работает следующее:

my $status = system(
    "gpg --pinentry-mode loopback --passphrase $passphrase --decrypt $my_encrypted_backup_file"
  . " | "
  . "mysql --host=myhost --user=myuser --password=mysecret mydatabase"
);

при условии отсутствия ошибок .

Однако, если во время первого процесса возникает ошибка (например, неверная $ passphrase), $status == 0, что ошибочно указывает на успех. Я понимаю, что это потому, что состояние возвращается из второго процесса, процесс MySQL.

Существует ли обобщенный способ, использующий system(), чтобы получить статус от всех собранных по конвейеру процессов или как-то обнаружить ошибку, если любой один такой процесс завершится неудачей?

Кстати, я сам протестировал gpg (без вывода его данных в mysql), и он возвращает код ошибки при вводе неверной фразы-пароля.

Обходным решением может быть некоторый флаг опции в mysql, который возвращает ошибку, когда ничего не получает от gpg. Другой обходной путь - разбить процессы и использовать какой-нибудь tmp-файл. Тем не менее, я хотел бы более обобщенное решение. Спасибо!

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Спасибо @Schwern за предложение IPC :: Run. Вот рабочее и проверенное решение:

use IPC::Run qw( run );

my $gpg = [
  "gpg",
  "--pinentry-mode=loopback",
  "--passphrase=$my_passphrase",
  "--decrypt",
  $my_backupfilepath
];

my $mysql = [
  "mysql",
  "--host=$mysql_host"
  "--user=$mysql_user"
  "--password=$mysql_pass"
  $mysql_dbname
];

run( $gpg, '|', $mysql ) || die "Error";

Мне все еще не удается записать подробное сообщение об ошибке, и я все еще вижу чрезмерно болтливый вывод gpg & mysql .... но, увы, я уже потратил достаточно времени на борьбу с Perl и GPG! Улучшения с радостью приняты.

Не имеет отношения к основному вопросу, но для любого, кто использует этот рецепт как есть ... чтобы получить GPG 2.1+ для принятия ключевой фразы через командную строку и чтобы не кэшировать ее, необходимо добавить следующее к gpg-agent.conf:

allow-loopback-pinentry
default-cache-ttl 1
max-cache-ttl 1

Источник: https://wiki.archlinux.org/index.php/GnuPG#pinentry

0 голосов
/ 07 января 2019

Если вам нужен такой точный контроль, не используйте оболочку.

Вызовы mysql можно заменить с помощью библиотек DBI и DBD :: mysql . gpg можно заменить на Crypt :: GPG .

Если это невозможно, выполните сам трубопровод с open и его режимами |- и -| .

open(
    my $gpg_out,
    "-|",
    "gpg --pinentry-mode loopback --passphrase $passphrase --decrypt $my_encrypted_backup_file"
) or die "Can't run gpg: $!";

open(
    my $mysql_in,
    "|-",
    "mysql --host=myhost --user=myuser --password=mysecret mydatabase"
) or die "Can't run mysql: $!";

while(my $line = <$gpg_out>) {
    print $mysql_in $line;
}

close $gpg_out;
close $mysql_in;
...