LINUX.ORG.RU

Ненормальная работа функции system()


0

0

В одной из прог мне необходимо было использовать функцию system() для передачи командному интерпретатору команды ls -l имя_файла, но почему- то вывод этой команды отображается раньше, чем текст, который по идее должнен быть перед её выводом. Код фрагмента проги, выполняющего вызов ls -l:

{
      printf("You don't have rights to read this file.\n"\
      "Rights for this file:");
      char b[]="ls -l ";
      system(strncat(b,argv[1],strlen(argv[1])));
      printf("\n");
      return 0;
   }
вывод проги:
[alex@95-65-17-135 ~]$ ./cloncat hreni 
You don't have rights to read this file.
----------. 1 alex alex 0 Май 16 22:39 hreni
Rights for this file:
[alex@95-65-17-135 ~]$ 
Спасибо всем, кто поможет понять, почему прога так работает и как это исправить.

★★★

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

Спасибо всем, кто поможет понять, почему прога так работает и как это исправить.

Такое происходит из-за буферизации. У твоей программы и у ls разные внутренние буферы. Чтобы обойти проблему - добавь вызов fflush на stdout после printf'а.

char b[]="ls -l "; 
system(strncat(b,argv[1],strlen(argv[1]))); 

А тут ошибка - в массиве b может быть недостаточно места для добавления второй строки.

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

А почему оно возникло? Можешь объяснить? Чтоб на будущее знал. Насколько я понял setbuf(stdout,NULL) должен исправить ситуацию?

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

Насколько я понял fflush(stdout) и setbuf(stdout,NULL) очищают буфер потока stdout(стандартный вывод)? Так?

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

Насколько я понял fflush(stdout) и setbuf(stdout,NULL) очищают буфер потока stdout(стандартный вывод)? Так?

fflush(stdout) «сбрасывает» содержимое буфера на стандартный вывод, а setbuf(stdout,NULL) вообще отключает буферизацию стандартного вывода.

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

Ну и как это обойти? *Лично я пока не заметил каких- либо огрех в исполнении.

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

и чем плох этот код:

printf("You don't have rights to read this file.\n"\
       "Rights for this file:\n");
       fflush(stdout);
       char b[]="ls -l ";
       system(strcat(b,argv[1]));
       return 0;
    }

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

Вы выделили под b столько места, сколько занимает строчка «ls -l », а потом к концу b (то есть, фактически, в память после b) записываете некоторое количество символов из argv[1]. писать в невыделенную память неправильно, это наверняка вызовет сегфолты(в более серьезных программах)

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

Ok. Как выделять память тогда? Не известна ни минимальная, ни максимальная длина аргумента.

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

Я бы сказал, что динамически, но наверно меня тогда закидают камнями. Динамическое выделение не всегда хорошо и не всегда разумно. Вообще максимальная длина пути в linux 2048 символов, буфер такого размера точно не переполнят

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

>setbuf(stdout, NULL);

Эта команда не имеет отношения к буферу, о котором я говорил (я про переменную b, а команда отключает буферизацию вывода). Нужно именно увеличить память, отводимую под b.

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

В linux с какой ФС? Это ж кажется и от ФС зависит,не? *За динамические не закидают. Просто писать тогда больше.. А это лень... Хотя...

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

2048 это то, что в ядре прописано. А насчет динамического выделения, кто-то из спецов по защите, уже не помню, рекомендовал юзать везде динамическое, чтобы во-первых, не переполнялись буфера, а во-вторых, не надо было запоминать их размеры.

Не знаю, в своих проектах делаю как припрет, а в коллективных обычно этот момент оговаривается.

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

Не, можно конечно делать код с исключительно динамическими переменными.. Но тогда там будет огромная мешанина new и delete'ов... А это снизит читабельность кода, что будет способствовать накоплению багов.

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

Не, можно конечно делать код с исключительно динамическими переменными.. Но тогда там будет огромная мешанина new и delete'ов... А это снизит читабельность кода, что будет способствовать накоплению багов.

Да, не парься, оставляй, как есть. Тебя благодарные пользователи, если таковые для твоей программы вообще найдутся, потом найдут и расстреляют.

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

>>Ok. Как выделять память тогда? Не известна ни минимальная, ни максимальная длина аргумента.

очень даже известна через strlen()

alex_custov ★★★★★
()

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

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main (int argc, char **argv)
{
  printf("You don't have rights to read this file.\n"\ 
	 "Rights for this file:\n"); 
  fflush(stdout); 
  char *buf = malloc (strlen ("ls -l ") + strlen (argv[1]));
  strcat (buf, "ls -l ");
  strcat (buf, argv[1]);
  system(buf);
  free (buf);
  return 0; 
}
age
()
Ответ на: комментарий от age

>malloc (strlen («ls -l ») + strlen (argv[1]));

Надо добавить 1 к размеру памяти под терминатор (нулевой символ \0). В остальном все верно.

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

Как-то так ИМХО:

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
	char *str = NULL;
	if (argc != 2) {
		printf("Usage: %s filename\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	printf("You don't have rights to read this file.\n" "Rights for this file:\n");
	fflush(stdout);
	if (asprintf(&str, "ls -l %s", argv[1]) == -1) {
		fprintf(stderr, "Memory allocation failed");
		exit(EXIT_FAILURE);
	}
	if (system(str) == -1) {
		perror("system");
		free(str);
		return EXIT_FAILURE;
	}
	free(str);
	return EXIT_SUCCESS;
}

tim239 ★★
()

>а sizeof(«ls -l ») - 1 это моветон?

Я почему-то думал, что sizeof - для типов. Забыл что для массивов тоже работает.

три ошибки же, если придраться - 4

Про недостаток места под \0 я уже знаю, а что еще? Укажите, пожалуйста.

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

+ в неинициализированную память, которую отдал malloc, сразу идёт strcat(), а он ищёт \0.

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

Иными словами, дабы не мучаться и не усложнять код, зная, что на длину пути в современных ФС ограничения не накладываются можно сделать так:

{
		printf("You don't have rights to read this file.\n"
		"Rights for this file:\n");
		//fflush(stdout);
		setbuf(stdout, NULL);
		char b[65535]="ls -l ";
		system(strcat(b,argv[1]));
		return 1;
	}

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

как параметр. Полный текст проги:

#include <stdio.h>
#include <unistd.h>
//unistd.h is needed for access() function
#include <string.h>
#include <stdlib.h>
//stdlib.h is needed for system() function
char a;
int main(int argc, char **argv)
{
	if(argc==1 || !strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")){
		printf("usage: proga filename\n"
		"-h or --help for help\n"
		"-v or --version displays version and exit\n");
		return 0;
	}
	else{
		if(!strcmp(argv[1],"-v") || (!strcmp(argv[1],"--version"))){
			printf("proga version 0.1\n");
		return 0;
	}
	else{
	if(access(argv[1],0)==-1){
		printf("File %s does not exist.\n",argv[1]);
		return 1;
	}
	else if(access(argv[1],4)==-1){
		printf("You don't have rights to read this file.\n"
		"Rights for this file:\n");
		//fflush(stdout);
		setbuf(stdout, NULL);
		char b[65535]="ls -l ";
		system(strcat(b,argv[1]));
		return 1;
	}
		FILE *f=fopen(argv[1],"r");
		while(feof(f)==0){
			a=fgetc(f);
			printf("%c",a);
		}
		fclose(f);
		return 0;
	}
}
}

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

Хорошо, моделируем ситуацию: За компом сидит юзвекрь, который хочет запустить эту прогу. Откуда я знаю, какой длины будет переданный проге аргумент? Ты можешь это предсказать для каждого конкретного случая?

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

Сплойты писать бесполезно. Прога ж перед тем, как что- то сделать проверяет, существует ли файл.

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

if(argc==1 || !strcmp(argv[1],"-h") || !strcmp(argv[1],"--help")){

А если файл будет называться '-h'? Да и вообще - какое-то страшнючее мясо из блоков if/else, без нормального форматирования я вообще запутался...

printf(«You don't have rights to read this file.\n» «Rights for this file:\n»);

Права ИМХО лучше получать через stat(2), а не вызовом ls =).

//fflush(stdout); setbuf(stdout, NULL);

fflush в данном случае логичнее. Но если хочется setbuf, то опять таки логичнее вызывать его до printf, где-нибудь в самом начале программы.

char b[65535]=«ls -l »;

А если в argv[1] окажется больше символов? =)

FILE *f=fopen(argv[1],«r»); while(feof(f)==0){

А если fopen вернёт NULL?

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

код тут ни при чём, «неизвестно сколько символов юзверь введёт» вообще к коду не имеет отношения, а имеет к пониманию того, как работают аргументы ком. строки.

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

Во-первых, твоей программе можно передать больше 65535 байт в качастве первого аргумента, и используя это либо заставить её завершиться, либо в перспективе запустить какой-нибудь вредоносный код. Есть интересная книжка на эту тему, «Искусство эксплойта» - там чуть ли не в первой главе такая же по смыслу программа (только с буфером в 50 символов, но это не принципиально).

Во-вторых, 65535 - это 65 килобайт целых! Нельзя так расходовать память без причины.

Ну а что вообще facepalm - такой жест, который используют, чтобы показать, как все запущенно =(

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

>>Откуда я знаю, какой длины будет переданный проге аргумент?

ты тролль? :))

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

Извини, но ты явно не в теме =( А количество символов в первом аргументе это strlen(argv[1])

tim239 ★★
()

Кстати говоря, если ты не хочешь возиться с malloc/free, пишешь на C (именно на C, а не на C++), да ещё и при этом используешь нормальный компилятор с поддержкой стандарта C99, то ты можешь использовать динамические массивы на стеке. Примерно так:

char strbuf[strlen(str1) + strlen(str2) + 1 /* '\0' */];
strcpy(strbuf, str1);
strcat(strbuf, str2);

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

Что есть исключительно трабла юзверя- ему ж вводить.

С таким подходом - не писать тебе программ сложнее хеллоу ворлда...

Deleted
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.