LINUX.ORG.RU

popen и двойные кавычки

 ,


0

2

из сишной программы я вызываю openssl sha1 <имя файла> (как ни странно это самый быстрый вариант ибо openssl хорошо оптимизирован)

строка вызова формировалась так:

sprintf(shellcmd,"openssl sha1 '%s'",filename);

все было хорошо пока не появились файлы вида:

хрень 'янь'.инь.rar

те содержащие одинарные кавычки

из командной строки катит такая форма

openssl sha1 "хрень 'янь'.инь.rar"

тк popen использует sh -c то я проверил конструкцию:

sh -c "openssl sha1 \"хрень 'янь'.инь.rar\""

я соответственно опробовал конструкцию формирования

sprintf(shellcmd,"openssl sha1 \\\"%s\\\"",filename);

но увы Ж( получаю обрывы строки на пробелах в имени файла

прошу подсказать как же тут победить вызов ?



Последнее исправление: Nagisa (всего исправлений: 1)

Оформи нормально сообщение

1) цитаты кода: www.linux.org.ru/help/lorcode.md

2) сам стиль изложения - ты не сообщил о том, в чём именно заключается проблема.

Чтобы слать произвольные аргументы программам в popen() или system() - надо экранировать в них кавычки и обратные слэши. Но лучше научиться использовать pipe()+fork()+dup2()+execve() - будет конечно длиннее код, зато никаких странных действий выполнять не придётся.

firkax ★★★★★
()

Но вообще запускать внешние проги для подсчёта sha1 не надо. Можешь взять либо подсчёт sha1 из того же openssl в виде so-библиотеки, либо, ещё лучше, вставить этот несложный алгоритм себе в прогу, просто скомпилируешь соответствующий модуль с -O2 и будет не медленнее чем openssl.

firkax ★★★★★
()
Ответ на: комментарий от Nagisa

Хм и правда, интересно почему так.

-O3 обычно бесполезен если код изначально нормальный без всякой тормозящей чуши, он отличается от -O2 в основном тем что пытается исправлять огрехи программиста.

Ну можно реально libcrypto.so слинковать и оттуда тот же openssl-ный sha1 вызывать но уже функцией а не прогой.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

надо экранировать в них кавычки и обратные слэши

понимаю что надо и соответственно пробовал это сделать, но результата не достиг

соответственно задаю вопрос - как именно ? те что еще надо добавить ?

я исходил из документации

The popen() function shall execute the command specified by the string command. It shall create a pipe between the calling program and the executed command, and shall return a pointer to a stream that can be used to either read from or write to the pipe.

The environment of the executed command shall be as if a child process were created within the popen() call using the fork() function, and the child invoked the sh utility using the call:

execl(shell path, "sh", "-c", command, (char *)0);

опробовал полное экранирование

sprintf(shellcmd,"\"openssl sha1 \\\"%s\\\"\"",filename);

но тоже

sh: openssl sha1 \"янь хрень.pdf\": No such file or directory

хотя прямой вызов полученной строки - катит

sh -c "openssl sha1 \"янь хрень.pdf\""

вот и вопрос - куда дальше ?

Nagisa
() автор топика
Ответ на: комментарий от firkax

Ну можно реально libcrypto.so слинковать и оттуда тот же openssl-ный sha1 вызывать но уже функцией а не прогой.

можно многое, но задача куда менее глобальна - достаточно разобраться с экранированием дабы имена с одинарными кавычками можно было обработать

я к тому, что советы в стиле - «перепиши всё» - несколько оффтопичны ибо вопрос-то про экранирование и только

прошу подсказать что именно я не учел в экранировании ?

Nagisa
() автор топика
Ответ на: комментарий от Nagisa

опробовал полное экранирование

sprintf(shellcmd,"\"openssl sha1 \\\"%s\\\"\"",filename);

Это не полное экранирование, экранировать надо всё, в том числе имя файла. Разбирай его посимвольно, видишь кавычки и слэши - экранируй. А, ну и кавычки вокруг команды зачем добавил? И кавычки вокруг имени файла зачем-то экранировал - это не надо было делать. Они тут синтаксис шелла и нужны как есть.

Только, повторю, вместо этой возни лучше использовать exec.

execl(shell path, «sh», "-c", command, (char *)0);

Это ты пытаешься эмулировать popen и получаешь все те же проблемы которые у него были. Бесполезное занятие. Запускать надо openssl безо всяких шеллов.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 4)
Ответ на: комментарий от firkax

Только, повторю, вместо этой возни лучше использовать exec.

тогда прошу подсказать как получить результат ? ибо в документации:

A successful call to execl does not have a return value because the new process image overlays the calling process image. However, a -1 is returned if the call to execl is unsuccessful.

Nagisa
() автор топика
Ответ на: комментарий от Nagisa

Ну я ж писал ещё в самом первом ответе - надо pipe+fork+dup2+exec, почему ты это проигнорил?

int pfd[2];
if(pipe(pfd)<0) { /* errno от pipe */ return -1; }
if((fr=fork())<0) { /* errno от fork */ close(pfd[0]); close(pfd[1]); return -1; }
if(!fr) {
  if(dup2(pfd[1],1)<0) { /* errno от dup2 */ _exit(-1); }
  execve(...);
  /* errno от exec */
  _exit(-1);
}
close(pfd[1]);
// читаем ответ из pfd[0]
waitpid(fr, &status, 0)
firkax ★★★★★
()
Ответ на: комментарий от Nagisa

а может тебе питон взять? я сейчас измерил евошный sha1 из модуля hashlib, на огромном файле получается даже чуточку быстрее, чем openssl:

OpenSSL:
    SHA1: 30af18a30ee0452e1306d8c060f02c232b7a88a6
    Time: 17.850159406661987 s
hashlib:
    SHA1: 30af18a30ee0452e1306d8c060f02c232b7a88a6
    Time: 17.349505186080933 s

питон на самом деле ту же libcrypto.so вызывает, но, судя по документации, это только если он собирался с openssl.

на мелких файлах преимущество использования .so против процесса openssl будет гораздо очевиднее.

anonymous
()
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *cmd = "openssl";
    char *args = "sha1";
    char q = '"';
    char *file = "file with spaces and 'quotes'.txt";
    char command[1024];
    sprintf(command, "%s %s %c%s%c", cmd, args, q, file, q);
    printf("command: %s\n", command);

    FILE *fp = popen(command, "r");
    if (fp == NULL) {
        perror("Ошибка при выполнении команды ls");
        return EXIT_FAILURE;
    }

    char buffer[1024];
    printf("result: ");
    while (fgets(buffer, sizeof(buffer), fp) != NULL) {
        printf("%s", buffer);
    }

    pclose(fp);

    return EXIT_SUCCESS;
}
$ echo testtesttest > "file with spaces and 'quotes'.txt"
$ cc tmp.c && ./a.out
command: openssl sha1 "file with spaces and 'quotes'.txt"
result: SHA1(file with spaces and 'quotes'.txt)= 532b9dc68b207640ea1517b4b2d70d8d45c00d58
iron ★★★★★
()
Ответ на: комментарий от theNamelessOne

Зато потом сюрприз будет, если в имени файла окажутся двойные кавычки…

Не вижу большой проблемы. Разбить имя файла на токены через strtok() и потом склеить в команде. Сложнее, да, но не невозможно. Но проще от таких имен файлов избавиться.

iron ★★★★★
()
Ответ на: комментарий от iron

Не вижу большой проблемы. Разбить имя файла на токены через strtok() и потом склеить в команде

Ну да, это же гораздо проще и логичней, чем использовать более подходящий инструмент типа того же execve.

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

более подходящий инструмент типа того же execve

Изначально ТЗ было о реализации выполнения через popen(). Насколько подходящий тот или иной инструмент – решает топикстартер исходя из своих задач, благо выше ему набросали вариантов.

iron ★★★★★
()
Ответ на: комментарий от Nagisa

можно многое, но задача куда менее глобальна - достаточно разобраться с экранированием дабы имена с одинарными кавычками можно было обработать

сегодня одни кавычки, завтра другие, потом обратные слеши, префиксные дефисы, $переменные, команды в backticks … что будешь со всем этим делать?

я бы не сказал, что обработка всех этих ситуаций менее «глобальна», чем написать одну простую функцию, дёргающую api libcrypto.

почему ты не хочешь использовать либу?

anonymous
()
Ответ на: комментарий от iron
@@ -8 +8 @@
-    char *file = "file with spaces and 'quotes'.txt";
+    char *file = "file with spaces and `ticks`.txt";
$ echo testtesttest > "file with spaces and \`ticks\`.txt" 
$ tcc -run tmp.c
command: openssl sha1 "file with spaces and `ticks`.txt"
sh: line 1: ticks: command not found

проще от таких имен файлов избавиться

да проще переписать вызов нормально или взять либу

anonymous
()
Ответ на: комментарий от anonymous

Как минимум у Паскаля, если судить по его пари, подход к религии был чисто прагматический. Я бы не пошёл в верующие люди на основании рассмотренного им аргумента, жлобский он какой-то.

Вот Ньютон — да, погружался серьёзно.

hobbit ★★★★★
()

Если приложение планируется использовать в какой-либо недоверенной среде, то лучше использовать библиотеку, потому что

  1. на экранирование всех возможных дурацких символов в названии файла уйдёт много времени и нервов;

  2. оригинальный исполняемый файл openssl можно простыми манипуляциями с переменной окружения PATH заменить на вредоносный скриптик, который, допустим, возвращает только то, что нужно злоумышленнику;

  3. при поставке программы нужно дополнительно запариваться о runtime-зависимости в лице бинарника openssl.

Что касается скорости, то вызов дочернего процесса, который выполняет какое-то действие, не может быть быстрее выполнения этого же действия напрямую. Наблюдаемый результат скорее всего является погрешностью измерения или утилита командной строки делает расчёт более производительным способом, чем это делали вы при сравнивании.

txgk
()
Ответ на: комментарий от hobbit

По личной аналогии - знаю, что такое классы, немного умею применять, но нафига они нужны?! не эффективный код получается :-)

один идет в церковь другой, покупает ливерную колбасу (c) ¯_(ツ)_/¯¯

anonymous
()
Ответ на: комментарий от anonymous

Пусть делает как делает. Потом будет хакерам раздолье и работа безопасникам. Это каких дел можно наворотить если он шел дёргать будет по именам файлов. Начать стоит с проверки

`yes`
файла, как оно там обрабатываться будет. Думаю отказ в обслуживании (всё зависнет, придёт OOM-killer поубивает половину процессов ОС) Потом проверить
$(yes)
файл или как, там, по памяти пишу, давно sh скрипты не делал

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 2)