溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

node.js怎么實現(xiàn)偽終端

發(fā)布時間:2022-12-01 09:19:55 來源:億速云 閱讀:170 作者:iii 欄目:開發(fā)技術(shù)

這篇“node.js怎么實現(xiàn)偽終端”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“node.js怎么實現(xiàn)偽終端”文章吧。

偽終端

偽終端不是真正的終端,而是內(nèi)核提供的一個“服務(wù)”。終端服務(wù)通常包括三層:

最頂層提供給字符設(shè)備的輸入輸出接口中間層的線路規(guī)程(line discipline)底層的硬件驅(qū)動

其中,最頂層的接口往往通過系統(tǒng)調(diào)用函數(shù)實現(xiàn),如(read,write);而底層的硬件驅(qū)動程序則負責偽終端的主從設(shè)備通信,它由內(nèi)核提供;線路規(guī)程看起來則比較抽象,但是實際上從功能上說它負責輸入輸出信息的“加工”,如處理輸入過程中的中斷字符(ctrl + c)以及一些回退字符(backspace 和 delete)等,同時轉(zhuǎn)換輸出的換行符n為rn等。

一個偽終端分為兩部分:主設(shè)備和從設(shè)備,他們底層通過實現(xiàn)默認線路規(guī)程的雙向管道連接(硬件驅(qū)動)。偽終端主設(shè)備的任何輸入都會反映到從設(shè)備上,反之亦然。從設(shè)備的輸出信息也通過管道發(fā)送給主設(shè)備,這樣可以在偽終端的從設(shè)備中執(zhí)行shell,完成終端的功能。

偽終端的從設(shè)備中,可以真實的模擬終端的tab補全和其他的shell特殊命令,因此在node原生模塊不能滿足需求的前提下,我們需要把目光放到底層,看看OS提供了什么功能。目前,glibc庫提供了posix_openpt接口,不過流程有些繁瑣:

使用posix_openpt打開一個偽終端主設(shè)備 grantpt設(shè)置從設(shè)備的權(quán)限 unlockpt解鎖對應(yīng)的從設(shè)備獲取從設(shè)備名稱(類似 /dev/pts/123)主(從)設(shè)備讀寫,執(zhí)行操作

因此出現(xiàn)了封裝更好的pty庫,僅僅通過一個forkpty函數(shù)便可以實現(xiàn)上述所有功能。通過編寫一個node的c++擴展模塊,搭配pty庫實現(xiàn)一個在偽終端從設(shè)備執(zhí)行命令行的terminal。

關(guān)于偽終端安全性的問題,我們在文章的最后在進行討論。

偽終端實現(xiàn)思路

根據(jù)偽終端的主從設(shè)備的特性,我們在主設(shè)備所在的父進程中管理偽終端的生命周期及其資源,在從設(shè)備所在的子進程中執(zhí)行shell,執(zhí)行過程中的信息及結(jié)果通過雙向管道傳輸給主設(shè)備,由主設(shè)備所在的進程向外提供stdout。

在此處借鑒pty.js的實現(xiàn)思路:

pid_t pid = pty_forkpty(&master, name, NULL, &winp);

 switch (pid) {
 case -1:
  return Nan::ThrowError("forkpty(3) failed.");
 case 0:
  if (strlen(cwd)) chdir(cwd);

  if (uid != -1 && gid != -1) {
  if (setgid(gid) == -1) {
   perror("setgid(2) failed.");
   _exit(1);
  }
  if (setuid(uid) == -1) {
   perror("setuid(2) failed.");
   _exit(1);
  }
  }

  pty_execvpe(argv[0], argv, env);

  perror("execvp(3) failed.");
  _exit(1);
 default:
  if (pty_nonblock(master) == -1) {
  return Nan::ThrowError("Could not set master fd to nonblocking.");
  }

  Local<Object> obj = Nan::New<Object>();
  Nan::Set(obj,
  Nan::New<String>("fd").ToLocalChecked(),
  Nan::New<Number>(master));
  Nan::Set(obj,
  Nan::New<String>("pid").ToLocalChecked(),
  Nan::New<Number>(pid));
  Nan::Set(obj,
  Nan::New<String>("pty").ToLocalChecked(),
  Nan::New<String>(name).ToLocalChecked());

  pty_baton *baton = new pty_baton();
  baton->exit_code = 0;
  baton->signal_code = 0;
  baton->cb.Reset(Local<Function>::Cast(info[8]));
  baton->pid = pid;
  baton->async.data = baton;

  uv_async_init(uv_default_loop(), &baton->async, pty_after_waitpid);

  uv_thread_create(&baton->tid, pty_waitpid, static_cast<void*>(baton));

  return info.GetReturnValue().Set(obj);
 }

以上就是關(guān)于“node.js怎么實現(xiàn)偽終端”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI