"простой" эмулятор терминала (Linux)

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

"простой" эмулятор терминала (Linux)

Сообщение coyot.rush » 05.02.2010 14:35:28

Предедущий мой пост остался без ответа http://www.freepascal.ru/forum/viewtopic.php?f=11&t=5356
Для перехвато вывода утилит вроде top нужен эмулятор терминала. Скачал исходники xterm. Посмотрел :) . Кажеться нехватает вызова openpty :idea: http://www.opennet.ru/man.shtml?topic=openpty&category=3&russian=0
Нашел еще алгорит использования http://linuxforum.ru/index.php?showtopic=16541
Код: Выделить всё
для этого юзаются псевдотерминалы.
алгоритм такой:
1) открываете master terminal device(man getpt || man posix_openpt || open("/dev/ptmx", O_RDWR))
2) unlo'чите терминал(man unlock)
3) получаете имя slave terminal device(man ptsname)
4) делаете fork()
5) в дочернем процессе открываете slave terminal device, используя имя, полученное ptsname
6) в дочернем: отвязываете процесс от терминала(man setsid)
7) в дочерернем: дублируете полученный дескриптор слейва в STDIN_FILENO, STDERR_FILENO и STDEOUT_FILENO (man dup2)
8) дальше(в том же дочернем процессе) идёт что-то вроде execl("/bin/bash", ..., NULL);
9) в дочернем: закрываем слейв
10) далее восновном процессе с помощью select совершаем нужные нам манипуляции с вводом/выводом

Кто нибуть что-то подобное писал на fpc :?: (Разумееться не считая MSE, автор сам еще его (терминал) допиливает :( )

Добавлено спустя 5 минут 17 секунд:
Смотрел исходники MSE на наличие строки openpty. Нет совпадений. Выходит я первый кто решил написвть эмулятор терминала на fpc :shock:
Аватара пользователя
coyot.rush
постоялец
 
Сообщения: 306
Зарегистрирован: 14.08.2009 08:59:48

Re: "простой" эмулятор терминала (Linux)

Сообщение Alexx2000 » 05.02.2010 16:01:00

coyot.rush писал(а):Выходит я первый кто решил написвть эмулятор терминала на fpc :shock:

Не первый :D B4rr4cuda этим уже занимался, сейчас его реализация используется в Double Commander, вот основной модуль реализующий работу терминала.
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 474
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: "простой" эмулятор терминала (Linux)

Сообщение coyot.rush » 05.02.2010 16:11:58

Сорри за офтопик Недавно скачал Double Commander, но там вызываеться xterm. Где там вызываеться свой терминал :?:
За ссылку спасибо,буду изучать.

Вот нашел новые "грабли"
Код: Выделить всё
procedure tmainfo.my_term(const sender: TObject);
var
id_term:PLongInt;
begin
libc.forkpty(id_term,Pchar('/usr/bin/free'),nil,nil);
end;


Линкер ругаеться

Код: Выделить всё
Free Pascal Compiler version 2.4.0 [2009/12/28] for i386
Copyright (c) 1993-2009 by Florian Klaempfl
Target OS: Linux for i386
Compiling get_console.pas
Compiling main.pas
....
Linking get_console
/usr/bin/ld: warning: link.res contains output sections; did you forget -T?
main.o: In function `TMAINFO__MY_TERM':
.../main.pas:157: undefined reference to `forkpty'
get_console.pas(11,1) Error: Error while linking
get_console.pas(11,1) Fatal: There were 1 errors compiling module, stopping
Fatal: Compilation aborted


Что за траблы
MSE 2.2+fpc2.4.0
Другие вызовы из libc вызываються без проблем :!:
Аватара пользователя
coyot.rush
постоялец
 
Сообщения: 306
Зарегистрирован: 14.08.2009 08:59:48

Re: "простой" эмулятор терминала (Linux)

Сообщение Alexx2000 » 05.02.2010 16:26:20

coyot.rush писал(а):Сорри за офтопик Недавно скачал Double Commander, но там вызываеться xterm. Где там вызываеться свой терминал :?:

Встроенный включается в настройках на странице "Вид окна" галочка "Консоль".
coyot.rush писал(а):Линкер ругаеться

Функция forkpty находится в либе libutil.a, соответственно необходимо её прилинковать. В приведенном мной выше примере это есть.
Я пробовал реализовать forkpty самостоятельно (пример во вложении), но моя реализация forkpty почему-то работает через раз, наверное что-то упустил при переводе с С.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 474
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: "простой" эмулятор терминала (Linux)

Сообщение coyot.rush » 05.02.2010 17:32:28

пишу
Код: Выделить всё
uses ...libutil;

Получаю нет такого модуля, но поиск дает что forkpty находиться в ptyh.inc
Код: Выделить всё
function openpty(__amaster:Plongint; __aslave:Plongint; __name:Pchar; __termp:Ptermios; __winp:Pwinsize):longint;cdecl;external clib name 'openpty';
function forkpty(__amaster:Plongint; __name:Pchar; __termp:Ptermios; __winp:Pwinsize):longint;cdecl;external clib name 'forkpty';


{ ---------------------------------------------------------------------
    Borland compatibility types
  ---------------------------------------------------------------------}

// Type

Модуля с именим libutil* у меня совсем нет. :?

Добавлено спустя 4 минуты 5 секунд:
Извиняюсь не досмотрел :oops:
Код: Выделить всё
{$L libutil.a} // under Linux forkpty is situated in libutil.a library


Добавлено спустя 32 минуты 23 секунды:
мистика нет у меня libutil* в системе. наверное что то не докачал :twisted:
Аватара пользователя
coyot.rush
постоялец
 
Сообщения: 306
Зарегистрирован: 14.08.2009 08:59:48

Re: "простой" эмулятор терминала (Linux)

Сообщение Alexx2000 » 05.02.2010 18:40:17

coyot.rush писал(а):мистика нет у меня libutil* в системе. наверное что то не докачал :twisted:

В Mandriva к примеру он находится в пакетe glibc-static, в некоторых дистрибутивах он входит в glibc-devel
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 474
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: "простой" эмулятор терминала (Linux)

Сообщение coyot.rush » 05.02.2010 19:32:13

glibc-devel стоит вот его содержание
Код: Выделить всё
/usr/include/gnu/stubs-32.h
/usr/lib/Mcrt1.o
/usr/lib/Scrt1.o
/usr/lib/crt1.o
/usr/lib/crti.o
/usr/lib/crtn.o
/usr/lib/gcrt1.o
/usr/lib/libBrokenLocale.so
/usr/lib/libanl.so
/usr/lib/libbsd-compat.a
/usr/lib/libbsd.a
/usr/lib/libc.so
/usr/lib/libc_nonshared.a
/usr/lib/libcidn.so
/usr/lib/libcrypt.so
/usr/lib/libdl.so
/usr/lib/libg.a
/usr/lib/libieee.a
/usr/lib/libm.so
/usr/lib/libmcheck.a
/usr/lib/libnsl.so
/usr/lib/libnss_compat.so
/usr/lib/libnss_dns.so
/usr/lib/libnss_files.so
/usr/lib/libnss_hesiod.so
/usr/lib/libnss_nis.so
/usr/lib/libnss_nisplus.so
/usr/lib/libpthread.so
/usr/lib/libpthread_nonshared.a
/usr/lib/libresolv.so
/usr/lib/librpcsvc.a
/usr/lib/librt.so
/usr/lib/libthread_db.so
/usr/lib/libutil.so
/usr/share/info/libc.info-1.gz
/usr/share/info/libc.info-10.gz
/usr/share/info/libc.info-11.gz
/usr/share/info/libc.info-2.gz
/usr/share/info/libc.info-3.gz
/usr/share/info/libc.info-4.gz
/usr/share/info/libc.info-5.gz
/usr/share/info/libc.info-6.gz
/usr/share/info/libc.info-7.gz
/usr/share/info/libc.info-8.gz
/usr/share/info/libc.info-9.gz
/usr/share/info/libc.info.gz

libutil.a нету :shock:
Дистрибутив Fedora 11 RF
Аватара пользователя
coyot.rush
постоялец
 
Сообщения: 306
Зарегистрирован: 14.08.2009 08:59:48

Re: "простой" эмулятор терминала (Linux)

Сообщение Alexx2000 » 05.02.2010 19:39:35

Под Fedora пакет называется glibc-static, вот например
Аватара пользователя
Alexx2000
постоялец
 
Сообщения: 474
Зарегистрирован: 25.10.2006 00:22:07
Откуда: Мытищи

Re: "простой" эмулятор терминала (Linux)

Сообщение coyot.rush » 11.02.2010 14:14:40

с реализаций B4rr4cuda я более и меннее разабоался. И даже перерхватил вывод от top :D осталось отфильтровать вывод на предмет спец симлов :lol:
только нашел новые "грабли", не могу корректо завершить приложение; испытывал на top
вот код слегка переделаного эмулятора терминала
Код: Выделить всё
unit emlinuxterm;
{$ifdef FPC}{$mode objfpc}{$h+}{$endif}
interface
uses
libc,sysutils;

//type

var
Fpty:LongInt;
id_term:LongInt;

function _Fork_pty_(const rows, cols: integer; const cmd:UTF8string; const params:UTF8string=''): System.THandle;
function _Read_Pty_(var str:UTF8String; timeout:timeval):longint;
function SendBreak_pty(): boolean;
function SendSignal_pty(Sig: integer): boolean;

{$L libutil.a} // under Linux forkpty is situated in libutil.a library
function forkpty(__amaster:Plongint; __name:Pchar; __termp:Ptermios; __winp:Pwinsize):longint;cdecl;external clib name 'forkpty';
function setenv(__name:Pchar; __value:Pchar; __replace:longint):longint;cdecl;external clib name 'setenv';
function execl(__path:Pchar; __arg:Pchar):longint;cdecl;varargs;external clib name 'execl';

const
terminal_x=132;
terminal_y=43;
max_xy_terminal=terminal_x*terminal_y;

const
  {c_cc characters}
  CDISABLE = 255;

           //key // xterm default bindings
  CINTR    = 003; // ^C
  CQUIT    = 034; // ^
  CERASE   = 177; // ^?
  CKILL    = 025; // ^U
  CEOF     = 004; // ^D
  CSTART   = 021; // ^Q
  CSTOP    = 023; // ^S
  CSUSP    = 032; // ^Z
  CREPRINT = 022; // ^R
  CWERASE  = 027; // ^W
  CLNEXT   = 026; // ^V
  CDISCARD = 017; // ^O

  //disabled
  CTIME    = 0;
  CMIN     = 1;
  CSWTC    = CDISABLE;
  CEOL     = CDISABLE;
  CEOL2    = CDISABLE;

implementation

//uses
//;


function _Fork_pty_(const rows, cols: integer; const cmd:UTF8string; const params:UTF8string=''): System.THandle;
var ws:TWinSize;
    ChildPid:THandle;
begin
  ws.ws_row:=rows;
  ws.ws_col:=cols;
  ws.ws_xpixel:=0;
  ws.ws_ypixel:=0;

   
  ChildPid:=forkpty(@Fpty,nil,nil,@ws);
 
  if ChildPid<0 then
  begin
    Result:=-1;
    Exit;
  end;

  if ChildPid=0 then
  begin
    //Child
    setenv('TERM', 'linux', 1);
   
    execl(pchar(cmd), pchar(params), nil);
    //execvp (PChar(Args[0]), @Args[0]);
    //если execl не сработал и новый процесс не подменил форкнутый, то ошибка
    WriteLn(pchar('execl() failed. Command: '+ cmd),length('execl() failed. Command: '+ cmd));
    //exit(127);  // error exec'ing
  end;
id_term:=ChildPid;
Result:=ChildPid;
end;



function _Read_Pty_(var str:UTF8String; timeout:timeval):longint;
var ifs:TFdSet;
    BytesRead:longint;
    buf:array [0..max_xy_terminal-1] of char;
begin

  Result:=0;

  if Fpty<0 then exit;
  //check if pty has new info for us
  FD_ZERO(ifs);
  FD_SET(Fpty,ifs);
  if Select(fpty+1,@ifs,nil,nil,timeout)<=0 then exit;
  bytesread := __read(fpty, buf, max_xy_terminal);

  result:=bytesread;
  str:='';
  if bytesread <= 0 then exit;
  str:=copy(buf,0,BytesRead);
end;


function SendSignal_pty(Sig: integer): boolean;
var BytesWritten:int64;
begin
  BytesWritten:=__write(Fpty,Sig,sizeof(sig));
  Result := (BytesWritten>0);
end;


function SendBreak_pty(): boolean;
begin
   result:=SendSignal_pty(CINTR);
end;

end.



Использую так
Код: Выделить всё
unit main;
{$ifdef FPC}{$mode objfpc}{$h+}{$endif}
interface
uses
mseglob,mseguiglob,mseguiintf,mseapplication,msestat,msemenus,msegui,
msegraphics,msegraphutils,mseevent,mseclasses,mseforms,msesimplewidgets,
msewidgets,msedataedits,mseedit,msestrings,msetypes,msegraphedits,msetimer;

type
tmainfo = class(tmainform)
   mini_term: tlabel;
   tgroupbox1: tgroupbox;
   b_run: tbutton;
   tlabel1: tlabel;
   tlabel2: tlabel;
   e_command: tstringedit;
   e_parametr: tstringedit;
   timer1: ttimer;
   b_kill: tbutton;
   l_debug: tlabel;
   tbutton1: tbutton;
   procedure read_pty(const sender: TObject);
   procedure run_sh(const sender: TObject);
   procedure kill_sh(const sender: TObject);
   procedure init_pr(const sender: TObject);
end;
var
mainfo: tmainfo;
implementation
uses
main_mfm,emlinuxterm,libc,sysutils;
//таймер
procedure tmainfo.read_pty(const sender: TObject);
var
str:utf8string;
t:timeval;
begin
t.tv_sec:=0;
t.tv_usec:=10;
if _Read_Pty_(str,t)>0 then mini_term.caption:=str;
end;
//запуск
procedure tmainfo.run_sh(const sender: TObject);
begin

id_term:=_Fork_pty_(terminal_x,terminal_y,e_command.value,e_parametr.value);
b_run.enabled:=false;
timer1.enabled:=true;
b_kill.enabled:=true;
l_debug.caption:='tId'+inttostr(id_term)+' ptyId'+inttostr(Fpty);
end;
//уничтожение процесса
procedure tmainfo.kill_sh(const sender: TObject);
begin
timer1.enabled:=false;
//SendBreak_pty();
//if Fpty>0 then __close(fpty);
if id_term>0 then kill(id_term,SIGKILL);
b_kill.enabled:=false;
b_run.enabled:=true;
mini_term.caption:='_';
end;

procedure tmainfo.init_pr(const sender: TObject);
begin
b_kill.enabled:=false;
end;

end.

Нажимаю килл (tmainfo.kill_sh) получаю зомби :?:
Естественно после закрытия приложения PID освобождаеться.

Где "грабли"?

PS:
может нужно инициалтзировать теримнал функцией tcsetattr.
пробовал , програмаа "вылетает"
Код: Выделить всё
procedure initterm;
var tio:termios;
begin
tcgetattr(Fpty,tio);
tio.c_iflag:=BRKINT or IGNPAR or ICRNL or IXON;
tio.c_oflag:=OPOST or ONLCR;
tio.c_cflag:=CS8 or CREAD;
tio.c_lflag:=ISIG or ICANON or IEXTEN or ECHO or ECHOE or ECHOK or ECHOKE or ECHOCTL;

tio.c_cc[VINTR]:=char(CINTR);
tio.c_cc[VQUIT]:=char(CQUIT);
tio.c_cc[VERASE]:=char(CERASE);
tio.c_cc[VKILL]:=char(CKILL);
tio.c_cc[VSTART]:=char(CSTART);
tio.c_cc[VSTOP]:=char(CSTOP);
tio.c_cc[VSUSP]:=char(CSUSP);
tio.c_cc[VREPRINT]:=char(CREPRINT);
tio.c_cc[VDISCARD]:=char(CDISCARD);
tio.c_cc[VWERASE]:=char(CWERASE);
tio.c_cc[VLNEXT]:=char(CLNEXT);
tio.c_cc[VEOF]:=char(CEOF);
tio.c_cc[VEOL]:=char(CEOL);
tio.c_cc[VEOL2]:=char(CEOL2);
tio.c_cc[VMIN]:=char(CMIN);
tio.c_cc[VTIME]:=char(CTIME);

tcsetattr(Fpty,TCSANOW,tio);
end;
******
ChildPid:=forkpty(@Fpty,nil,nil,@ws);
  initterm; 

  if ChildPid<0 then
******



Добавлено спустя 3 часа 42 минуты 7 секунд:
разабрался оказвыеться все просто :P
Код: Выделить всё
if id_term>0 then kill(id_term,SIGKILL);
waitpid(id_term,status_,WNOHANG);
b_kill.enabled:=false;

и зомби исчезают :wink:
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Аватара пользователя
coyot.rush
постоялец
 
Сообщения: 306
Зарегистрирован: 14.08.2009 08:59:48


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

Рейтинг@Mail.ru