Войти

Показать полную графическую версию : нестандартный output


Guest
28-05-2004, 16:21
Хочу сделать так:с помощью функции 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
Если я правильно понял логику Вашей программы, то ошибка в том, что dup2 вызывается после popen. Поскольку popen по сути fork, дескрипторы будут унаследованы нормальные (на stdout), а не переопределенные (в файл). И зачем Вы перенаправляете stdin (id=0)? Наверное, надо 1 и 2.

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

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

hasherfrog
04-06-2004, 10:11
Неправильно указаны аргументы у 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
Вроде работает!
Скорее всего из-за dup2() - но ведь в мане сказано что первый параметр - это старый дескриптор,а второй - новый,на который надо все перенаправлять!
И лучше использовать int lp=open()?

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

Guest
04-06-2004, 16:46
Теперь у меня такая трабла:Я хочу ессно это с сетью связать,т.е типа 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
0. Пользуйтесь тегом {{code}}
1. Приведите код клиента
2. Дескриптор 1 - stdout. В него только пишут. Вы из него читаете. То же касается и 2 - stderr.
3. Эффект зависания, кстати, будет всегда, поскольку Вы используете синхронную передачу данных. В результате, Ваша программа сервер будет жрать 100% CPU, если я ничего не путаю. Я пользуюсь select в таких случаях.
4. Брокен пип вылезает, очевидно, поле Ctrll+C? Если да, то так и должно быть, имхо.

Guest
07-06-2004, 17:20
Это клиент;

#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);
}


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

hasherfrog
09-06-2004, 10:08
Зачем bind в клиенте?
...

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

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

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
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
Насчёт получения только одной строки из ожидаемого множества. Тут уже немного зыбкая почва. Не пробовал. Точнее, не пробовал именно так. По-другому я делал. у меня тоже была проблема - приходила только одна строка, потом был длительный таймаут, потом я просил ещё рез и тут приходила куча - и задержавшиеся сообщения, и новые. Но это потому программа основывалась на 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);
Очень похоже на man select. Разберётесь, ладно? Я сейчас занимаюсь очень похожей проблемой, только у меня всё гораздо сложней.

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

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

Guest
10-06-2004, 11:55
ок,попробую разобратся......

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

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

Guest
11-06-2004, 16:27
Так 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
Всё ходим вокруг одного и того же.
do {
 if (select(...) == -1)
 break; //закрылся /bin/sh
 recv(...)
 printf(...);
} while (1); //пока не закроется /bin/sh
Вы не сможете узнать о закрытии конкретной отфоркнутой от /bin/sh программы. Вы можете только читать вывод всех программ, выполняющихся в рамках \bin\sh.

 

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




© OSzone.net 2001-2012