Компьютерный форум OSzone.net  

Компьютерный форум OSzone.net (http://forum.oszone.net/index.php)
-   Программирование и базы данных (http://forum.oszone.net/forumdisplay.php?f=21)
-   -   нестандартный output (http://forum.oszone.net/showthread.php?t=29667)

Guest 28-05-2004 16:21 205222

Хочу сделать так:с помощью функции popen() открыть /bin/bash на запись,записать туда допустим "uname -r " но вывод uname -r произвести не на stdout ,а на другой ф.дескриптор с помощью dup2():
printf(">>>> Opening stream popen <<<<\n");
FILE *fp,*lp;
lp=fopen("output","w+");
char ch[20];
fp=popen("/bin/bash","w");
printf("Enter command\n");
scanf("%s",&ch);
dup2(0,lp);
dup2(1,lp);
fprintf(fp,"%s",ch);
pclose(fp);
Но все равно выводится на экран а не в файл output!


*

hasherfrog 03-06-2004 15:12 205223

Если я правильно понял логику Вашей программы, то ошибка в том, что dup2 вызывается после popen. Поскольку popen по сути fork, дескрипторы будут унаследованы нормальные (на stdout), а не переопределенные (в файл). И зачем Вы перенаправляете stdin (id=0)? Наверное, надо 1 и 2.

Guest 03-06-2004 16:51 205224

Я последовал вашему совету,но результат тот же!
Даже не работает такой код:
FILE *fp;
fp=fopen("output","w");
dup2(1,fp)
dup2(2,fp);

printf("HEllo");
почему так?Все равно на экран выводит!

hasherfrog 04-06-2004 10:11 205225

Неправильно указаны аргументы у dup2
Код:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
 
int main (void)
{
  int fd = open("out.txt", O_CREAT | O_APPEND | O_WRONLY);
  dup2(fd, 1);
  printf("hello\n");
  close(fd);
  return 0;
}

Обратите внимание, что в примере я работаю с open, потому что dup2 работает с дескрипторами, а не FILE*

Guest 04-06-2004 12:34 205226

Вроде работает!
Скорее всего из-за dup2() - но ведь в мане сказано что первый параметр - это старый дескриптор,а второй - новый,на который надо все перенаправлять!
И лучше использовать int lp=open()?

hasherfrog 04-06-2004 13:21 205227

В мане немного по-дургому написано. dup2  делает  newfd  копией  oldfd,  закрывая newfd, если это необходимо. Недопонимание возникает, если не знать, как работает просто dup(), а не dup2(). Так исторически сложилось...
Насчёт open и fopen - трудно сказть, что лучше. open универсальнее, но с ней немного труднее работать.

Guest 04-06-2004 16:46 205228

Теперь у меня такая трабла:Я хочу ессно это с сетью связать,т.е типа telnet'a
сделать:
Сервак:
#define PORT1 1234
#define PORT2 4321
int main (void) {
int acc;
char buf[64];
int from_len;
FILE *fp;
fp=popen("/bin/sh","w");//открываем /bin/sh
      struct sockaddr_in serv,client;
int s;
s=socket(AF_INET,SOCK_STREAM,0);


serv.sin_family=AF_INET;
serv.sin_addr.s_addr=INADDR_ANY;
serv.sin_port=htons(PORT1);
bind(s,&serv,sizeof(serv));
int lis=listen(s,10);

while(1) {
       from_len=sizeof(client);
acc=accept(s,&client,sizeof(client));//принимаем коннект
dup2(acc,1);//перенаправляем i/o
dup2(acc,2);//
while(1) {
        from_len=read(acc,buf,64);//принимаем команды и записываем в buf
        if(buf=="killbill") {//если получаем команду "killbill" то выходим
                break;
        }
        fprintf(fp,"%s",buf);//записываем команду в /bin/sh
        perror("Status");
}
break;
exit(0);
}
"stream.c" 44L, 791C written

Клиент выглядит так же - цикл с посылкой и отправкой команды - но ничего не происходит!Как будто обе проги виснут!Иногда вылезает еррор Broken pipe

hasherfrog 07-06-2004 09:34 205229

0. Пользуйтесь тегом {{code}}
1. Приведите код клиента
2. Дескриптор 1 - stdout. В него только пишут. Вы из него читаете. То же касается и 2 - stderr.
3. Эффект зависания, кстати, будет всегда, поскольку Вы используете синхронную передачу данных. В результате, Ваша программа сервер будет жрать 100% CPU, если я ничего не путаю. Я пользуюсь select в таких случаях.
4. Брокен пип вылезает, очевидно, поле Ctrll+C? Если да, то так и должно быть, имхо.

Guest 07-06-2004 17:20 205230

Это клиент;
Код:

#define PORT1 1234
#define PORT2 4321
int main() {
struct sockaddr_in serv,cli;
int sock;
char buf[64],recved[64];
int from_len;
sock=socket(AF_INET,SOCK_STREAM,0);//создаем сокет
serv.sin_family=AF_INET;
serv.sin_port=htons(PORT2);
serv.sin_addr.s_addr=INADDR_ANY;
if(bind(sock,&serv,sizeof(serv)) < 0 )//биндим клиент
{ printf("Error binding\n");
 * * * *exit(-1);
}
cli.sin_family=AF_INET;
cli.sin_port=htons(PORT1);
cli.sin_addr.s_addr=inet_addr("127.0.0.1");
int conn=connect(sock,&cli,sizeof(cli));//коннектимся
if(conn) {
 * * * *printf("Connected\n");
}
while(1) {//цикл отправки и принятия команды
 * * * *printf("\nmy_bash$");
scanf("%s",&buf);//считываем команду
send(sock,buf,sizeof(buf),0);//отправляем команду
from_len=recv(sock,recved,sizeof(recved),0);//получаем результат
write(1,recved,from_len);//выводим результат на зкран
perror("Status");
if(buf=="killbill")//если команда killbill то выходим
{break;}
}
close(sock);

}

Клиент вроде правильно сделан

Я немного переделал сервер,но все равно глючит:
Код:

while(1) {
 * * * * lp=fopen("temp","r+");
 * * * * dup2(lp,1);
 * * * * dup2(lp,2);
 * * * * from_len=read(acc,buf,64);
 * * * * if(buf=="killbill") {
 * * * * * * * * break;
 * * * * }
 * * * * fprintf(fp,"%s",buf);
 * * * * perror("Status");
 * * * * fscanf(lp,"%s",&postbuf);
 * * * * send(acc,postbuf,sizeof(postbuf),0);
 * * * * fclose(lp);
 }

[s]Исправлено: Prisoner, 1:09 8-06-2004[/s]

hasherfrog 09-06-2004 10:08 205231

Зачем bind в клиенте?
...

Что-то я начинаю терять мысль. Вы мне даёте всё больше и больше кода, но какими-то кусками.Я по обрывкам кода не могу догадаться "а где тут глючит?" Давайте поконкретнее. У Вас проблема с сокетами или с пайпами? Это хоть и из одной оперы, но разные вещи.

Guest 09-06-2004 16:10 205232

Я сам запутался.....
Прежде я решил сделать все сначала(ну в смысле не переделать)
Сперва я сделал клиент - сервер,клиент просто передает одно сообщение(строку) серверу.Дальше я решил сделать цикл отправки и приема сообщений .Вроде получается.
Но вот пара вопросов:
Почему на код
Код:

if(strcmp(s,"kill")==0) {
       printf("Exiting...\n");
exit(0)
}

Где s - строка для посылки,он после первого вызова send() пишет Segmentation fault.
Мне тогда выход из кллиента не сделать!
И еще один вопрос - если я скажем сервер запустил,потом послал ему строку "kill",сервер выключился,то если сразу его врубить то он пишет "BIND() :Address already in use" и приходится просто немного ждать!Я ведь делаю в конце программы
close(s)
close(s_new)
shutdown(....);
в чем дело?
Сейчас я постараюсь сам решить вопрос чтобы не сбивать с толко вас и себя....

Добавлено:

так проблемму решил,т.е теперь клиент - сервак свободно обмениваются сообщениями.

Guest 09-06-2004 19:25 205233

2hasherfrog: Ура почти получилось!
Теперь если я скажем посылаю серверу команду "uname" он обратно посылает "Linux"
Но теперь вот что.Я максимум могу принимать одну строку,поэтому команда df вернет мне всего лишь одну строчку.Это происходит потому что я вызываю recv() только один раз - мне как я понял нужно сделать цикл с приемом сообщения....только вот не получается!
Вот сервер в "кратком содержании":
Код:

.....
s_new=accept(..);
....
while(1) {
fp=popen("/bin/sh","w");
dup2(s_new,1);
dup2(s_new,2);
recv=(s,recved,sizeof(recved),0);//принимаем команду
fprintf(fp,"%s",recved);//пишем в /bin/sh а он автоматом отправляет на сокет


Теперь клиент


Код:

while(1) {
printf("Enter command:");
scanf("%s",input);
send(s,input,sizeof(input),0);//посылаем команду
recv(s,recved,sizeof(recved),0);//принимаем результат
}

вот в клиенте как мне организовать цикл с приемом сообщения?

hasherfrog 10-06-2004 10:10 205234

Насчёт получения только одной строки из ожидаемого множества. Тут уже немного зыбкая почва. Не пробовал. Точнее, не пробовал именно так. По-другому я делал. у меня тоже была проблема - приходила только одна строка, потом был длительный таймаут, потом я просил ещё рез и тут приходила куча - и задержавшиеся сообщения, и новые. Но это потому программа основывалась на QProcess и там странно организована работа с пайпами. Поскольку в данном случае всё "своё", надо подумать... Может дело в самих пайпах, а может, в неправильной работе в ними.

Как вариант 1: может, надо добавить флаг  MSG_WAITALL в recv()?
Как вариант 2: Работайте с select.
Вот кусок моего старого кода от логгера:
Код:

   do {
        ...
        //listen child error messages
        if (!bPipeIsBroken)
        {
            //starts listen child
            fd_set rfds;
            struct timeval tv;
            int retval;
 
            FD_ZERO(&rfds);
            FD_SET(fd[0], &rfds);
            tv.tv_sec = 1;
            tv.tv_usec = 100;
 
            retval = select(fd[0] + 1, &rfds, NULL, NULL, &tv);
            // if (retval == -1)  -> broken pipe?
            if (retval == -1) break;
            // if (retval == 0) -> no data at input
            if (retval == 0) continue;
 
            char buffer[512];
            ssize_t msg_len = read(fd[0], buffer, 512);
            if ((msg_len == -1)  ||  (msg_len == 0))
            {
                bPipeIsBroken = 1;  //$$
                //if (g_bVerbouse) printf("Pipe seems to be broken\n"); //$$
                continue; //error, but not SIGPIPE //$$
            }
 
            buffer[msg_len] = 0;
            //Ucop checks only stderr channel.Thus, if program-child will ask something or
            //requests input from user, its question will be displayed. Lucky hit!
            logger.fprintf("%s", buffer);
        }
    } while (1);

Очень похоже на [s]man select[/s]. Разберётесь, ладно? Я сейчас занимаюсь очень похожей проблемой, только у меня всё гораздо сложней.

ЗЫ. Нда-с, не совсем понятно. fd[1] - конец трубы, из которой должна идти инфа.
logger - класс логгера, не обращайте внимания.

[s]Исправлено: hasherfrog, 10:13 10-06-2004[/s]

Guest 10-06-2004 11:55 205235

ок,попробую разобратся......

Guest 11-06-2004 14:24 205236

У меня еще такой вопрос(надеюсь последний=))
После того как popen запускает прогу ,то после завершения проги посылается какой-нибудь флаг типа EOF или еще что-то?Так было бы очень удобно,ведь я б знал до какого места считывать строки из сокета...

hasherfrog 11-06-2004 14:37 205237

Вообще-то, хм... popen - это по сути, fork. Какая программа должна завершиться первой? Та, которая отпочковалась или та, которая слушает процесс?
В приведённом мной последнем куске кода строка if (retval == -1) break прекращает слушание, потому что программа-потомок судя по всему, окончила работу. feof для FILE* при этом вернёт true, кстати. Но лучше не рассчитывать на FILE*, а работать с select'ом Ей можно после этого выходить. Если же основная программа хочет выйти первой, то она просто должна посиснуть на проводе с помощью pclose. как pclose вернёт управление - потомка нет, можно и самой уходить.
Надеюсь, мысль понятна...

Guest 11-06-2004 16:27 205238

Так popen у меня запускается,и он я запускаю /bin/sh,которая вырубится если я пошлю скажем команду killpopen.А та утилита,которую я запускаю с помощью sh и должна что то вернуть в конце!Вот к чему я клоню!
У меня же такая схема:
Сервак получает команду,записывает ее в fp,там /bin/sh форкает прогу какую-нибудь,прога все выводит на сокет - я со стороны клиента должен все что она вывела принять....... только я не знаю СКОЛЬКО раз мне нужно вызвать функцию recv,ведь мне данные приходят по строкам!
а если бы в конце прога что-то посылала,то я мог бы сделать что-то вроде
while(recved!=EOF) {
recv(s,recved,sizeof(recved),0);
printf("%s",recved);
}
Вот!
p.s. ваша мысль плохо понятна(в смысле я плохо понимаю....)

Добавлено:

Это похоже примерно на это:
сервак:
char first[20]="aaaaaaaa";
char second[20]="bbbbbbb";
char third[20]="EOF";
send(first);
send(second);
send(third);
клиент:
while(recved!="EOF") {
recv(&recved);
printf(recved);
}

hasherfrog 11-06-2004 16:36 205239

Всё ходим вокруг одного и того же.
Код:

do {
  if (select(...) == -1)
  break; //закрылся /bin/sh
  recv(...)
  printf(...);
} while (1); //пока не закроется /bin/sh

Вы не сможете узнать о закрытии конкретной отфоркнутой от /bin/sh программы. Вы можете только читать вывод всех программ, выполняющихся в рамках \bin\sh.

 

Guest 11-06-2004 16:42 205240

Я вас начинаю понимать!
Возможно я просто использую неудобный механизм передачи - мысль с завершением самой /bin/sh понятна.т.е после записи команды в /bin/sh,я вырубаю коннект(pclose(fp)),соответственно на клиенте вырубается дескриптор сокета(select) а что бы послать следующую команду,нужно просто заново присоединится с серваком,и повторить цикл.......только бы это щас реализовать.........
(p.s. надо бы зарегистрироватся...=))


Время: 17:03.

Время: 17:03.
© OSzone.net 2001-