এই উত্তরটি আমার নিজের বোঝার স্পষ্টতা হিসাবে সরবরাহ করা হয়েছে এবং আমার আগে @ স্টাফেনচাজেলাস এবং @ মাইক্রোজার দ্বারা অনুপ্রাণিত হয়েছেন।
টি এল; ডিআর
bash
বাহ্যিক সাহায্য ব্যতীত এটি করা সম্ভব নয় ;
- এটি করার সঠিক উপায় সাথে আছেন পাঠান টার্মিনাল ইনপুট
ioctl
কিন্তু
- সবচেয়ে সহজ কার্যক্ষম
bash
সমাধান ব্যবহার করে bind
।
সহজ সমাধান
bind '"\e[0n": "ls -l"'; printf '\e[5n'
বাশের একটি শেল বিল্টিন রয়েছে যা bind
একটি শেল কমান্ড কার্যকর করার জন্য একটি কী সিকোয়েন্স প্রাপ্ত হলে অনুমতি দেয়। সংক্ষেপে, শেল কমান্ডের আউটপুট শেলের ইনপুট বাফারে লেখা হয়।
$ bind '"\e[0n": "ls -l"'
মূল সিকোয়েন্স \e[0n
( <ESC>[0n
) হ'ল একটি এএনএসআই টার্মিনাল এস্কেপ কোড যা কোনও টার্মিনাল প্রেরণ করে যে এটি স্বাভাবিকভাবে কাজ করছে indicate এটি কোনও ডিভাইস স্থিতি প্রতিবেদনের অনুরোধ হিসাবে এটি প্রেরণ করে যা প্রেরণ করা হয় <ESC>[5n
।
echo
পাঠ্যটিকে ইনজেক্ট করতে দেয় এমন কোনও প্রতিক্রিয়াকে আবদ্ধ করে আমরা যখনই ডিভাইসের স্থিতির অনুরোধ করে এটি পাঠাতে পারি এবং এটি একটি <ESC>[5n
পালানোর ক্রম পাঠিয়ে সম্পন্ন করে ।
printf '\e[5n'
এটি কাজ করে এবং সম্ভবত মূল প্রশ্নের উত্তর দেওয়ার পক্ষে যথেষ্ট কারণ অন্য কোনও সরঞ্জাম জড়িত নেই। এটি খাঁটি bash
তবে একটি ভাল আচরণের টার্মিনালের উপর নির্ভর করে (কার্যত সবগুলিই রয়েছে)।
এটি কমান্ড লাইনে প্রতিধ্বনিত পাঠ্যটি ব্যবহার করতে প্রস্তুত যেমন এটি টাইপ করা হয়েছে been এটি সংযোজন, সম্পাদনা করা এবং টিপে ENTER
এটি কার্যকর করা যায়।
যোগ \n
আবদ্ধ কমান্ড এটি স্বয়ংক্রিয়ভাবে মৃত্যুদন্ড কার্যকর আছে।
তবে এই সমাধানটি কেবলমাত্র বর্তমান টার্মিনালে (যা মূল প্রশ্নের ক্ষেত্রের মধ্যে রয়েছে) কাজ করে। এটি একটি ইন্টারেক্টিভ প্রম্পট থেকে বা একটি উত্সাহিত স্ক্রিপ্ট থেকে কাজ করে তবে সাবসেল থেকে ব্যবহৃত হলে এটি একটি ত্রুটি উত্থাপন করে:
bind: warning: line editing not enabled
পরবর্তী বর্ণিত সঠিক সমাধানটি আরও নমনীয় তবে এটি বাহ্যিক আদেশের উপর নির্ভর করে।
সঠিক সমাধান
ইনপুট ইনজেক্ট করার সঠিক উপায়টি tty_ioctl ব্যবহার করে , I / O নিয়ন্ত্রণের জন্য একটি ইউনিক্স সিস্টেম কল যার মধ্যে একটি TIOCSTI
কমান্ড রয়েছে যা ইনপুট ইনজেক্ট করতে ব্যবহার করা যেতে পারে।
TIOC থেকে " টি erminal আইওসি TL " এবং STI "থেকে এস শেষ টি erminal আমি nput "।
এর জন্য কোনও আদেশই অন্তর্নিহিত হয়নি bash
; এটি করার জন্য একটি বাহ্যিক আদেশ প্রয়োজন। সাধারণ জিএনইউ / লিনাক্স বিতরণে এমন কোনও কমান্ড নেই তবে সামান্য প্রোগ্রামিং সহ এটি অর্জন করা কঠিন নয় difficult এখানে একটি শেল ফাংশন রয়েছে যা ব্যবহার করে perl
:
function inject() {
perl -e 'ioctl(STDIN, 0x5412, $_) for split "", join " ", @ARGV' "$@"
}
এখানে, কমান্ডের 0x5412
জন্য কোড TIOCSTI
।
TIOCSTI
মান সহ স্ট্যান্ডার্ড সি হেডার ফাইলগুলিতে একটি ধ্রুবক সংজ্ঞায়িত 0x5412
। চেষ্টা করুন grep -r TIOCSTI /usr/include
, বা দেখুন /usr/include/asm-generic/ioctls.h
; এটি পরোক্ষভাবে সি প্রোগ্রামগুলিতে অন্তর্ভুক্ত #include <sys/ioctl.h>
।
তারপরে আপনি এটি করতে পারেন:
$ inject ls -l
ls -l$ ls -l <- cursor here
কিছু অন্যান্য ভাষায় বাস্তবায়ন নীচে দেখানো হয়েছে (একটি ফাইল এবং তারপরে chmod +x
এটি সংরক্ষণ করুন):
পার্ল inject.pl
#!/usr/bin/perl
ioctl(STDIN, 0x5412, $_) for split "", join " ", @ARGV
আপনি সংখ্যার মানটি ব্যবহার করার পরিবর্তে sys/ioctl.ph
কোনটি সংজ্ঞা দেয় তা উত্পন্ন করতে পারবেন TIOCSTI
। এখানে দেখুন
পাইথন inject.py
#!/usr/bin/python
import fcntl, sys, termios
del sys.argv[0]
for c in ' '.join(sys.argv):
fcntl.ioctl(sys.stdin, termios.TIOCSTI, c)
চুনি inject.rb
#!/usr/bin/ruby
ARGV.join(' ').split('').each { |c| $stdin.ioctl(0x5412,c) }
সি inject.c
সঙ্গে সংকলন gcc -o inject inject.c
#include <sys/ioctl.h>
int main(int argc, char *argv[])
{
int a,c;
for (a=1, c=0; a< argc; c=0 )
{
while (argv[a][c])
ioctl(0, TIOCSTI, &argv[a][c++]);
if (++a < argc) ioctl(0, TIOCSTI," ");
}
return 0;
}
**! ** এখানে আরও উদাহরণ রয়েছে ।
এটি ব্যবহার ioctl
করে সাবস্কেলগুলিতে কাজ করে। এটি পরবর্তী ব্যাখ্যা অনুসারে অন্যান্য টার্মিনালগুলিতেও ইনজেকশন করতে পারে।
এটি আরও গ্রহণ করা (অন্যান্য টার্মিনালগুলি নিয়ন্ত্রণ করা)
এটি মূল প্রশ্নের ক্ষেত্রের বাইরে তবে উপযুক্ত অনুমতি থাকা সাপেক্ষে অন্য একটি টার্মিনালে অক্ষরগুলি ইনজেক্ট করা সম্ভব। সাধারণত এর অর্থ হচ্ছে root
, তবে অন্যান্য উপায়ের জন্য নীচে দেখুন।
অন্য একটি টার্মিনালের tty নির্দিষ্ট করে একটি কমান্ড-লাইন আর্গুমেন্ট গ্রহণ করতে উপরে প্রদত্ত সি প্রোগ্রামটি বাড়ানো সেই টার্মিনালে ইনজেকশনের অনুমতি দেয়:
#include <stdlib.h>
#include <argp.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
const char *argp_program_version ="inject - see https://unix.stackexchange.com/q/213799";
static char doc[] = "inject - write to terminal input stream";
static struct argp_option options[] = {
{ "tty", 't', "TTY", 0, "target tty (defaults to current)"},
{ "nonl", 'n', 0, 0, "do not output the trailing newline"},
{ 0 }
};
struct arguments
{
int fd, nl, next;
};
static error_t parse_opt(int key, char *arg, struct argp_state *state) {
struct arguments *arguments = state->input;
switch (key)
{
case 't': arguments->fd = open(arg, O_WRONLY|O_NONBLOCK);
if (arguments->fd > 0)
break;
else
return EINVAL;
case 'n': arguments->nl = 0; break;
case ARGP_KEY_ARGS: arguments->next = state->next; return 0;
default: return ARGP_ERR_UNKNOWN;
}
return 0;
}
static struct argp argp = { options, parse_opt, 0, doc };
static struct arguments arguments;
static void inject(char c)
{
ioctl(arguments.fd, TIOCSTI, &c);
}
int main(int argc, char *argv[])
{
arguments.fd=0;
arguments.nl='\n';
if (argp_parse (&argp, argc, argv, 0, 0, &arguments))
{
perror("Error");
exit(errno);
}
int a,c;
for (a=arguments.next, c=0; a< argc; c=0 )
{
while (argv[a][c])
inject (argv[a][c++]);
if (++a < argc) inject(' ');
}
if (arguments.nl) inject(arguments.nl);
return 0;
}
এটি ডিফল্টরূপে একটি নিউলাইনও প্রেরণ করে তবে এর মতো echo
এটি -n
এটি দমন করার জন্য একটি বিকল্প সরবরাহ করে। --t
অথবা --tty
বিকল্প একটি আর্গুমেন্ট প্রয়োজন - tty
টার্মিনাল ইনজেকশনের হবে। এর জন্য মানটি সেই টার্মিনালে পাওয়া যাবে:
$ tty
/dev/pts/20
এটি দিয়ে সংকলন gcc -o inject inject.c
। --
কমান্ড-লাইন অপশনের ভুল ব্যাখ্যা করার যুক্তি পার্সারটিকে আটকানোর জন্য কোনও হাইফেন রয়েছে কিনা তা সহ পাঠ্যটিকে ইনজেক্ট করতে উপসর্গ করুন। দেখুন ./inject --help
। এটি এর মতো ব্যবহার করুন:
$ inject --tty /dev/pts/22 -- ls -lrt
বা শুধু
$ inject -- ls -lrt
বর্তমান টার্মিনাল ইনজেক্ট করতে।
অন্য টার্মিনালে ইনজেকশনের জন্য প্রশাসনিক অধিকার প্রয়োজন যা দ্বারা প্রাপ্ত করা যেতে পারে:
- কমান্ড জারি হিসাবে
root
,
- ব্যবহার
sudo
,
- থাকার
CAP_SYS_ADMIN
সামর্থ্য বা
- এক্সিকিউটেবল সেট
setuid
নিয়োগের জন্য CAP_SYS_ADMIN
:
$ sudo setcap cap_sys_admin+ep inject
নিয়োগের জন্য setuid
:
$ sudo chown root:root inject
$ sudo chmod u+s inject
পরিষ্কার আউটপুট
ইনজেক্টেড পাঠ্যটি প্রম্পটের সামনে উপস্থিত হয় যেন প্রম্পট প্রদর্শিত হওয়ার আগে টাইপ করা হয়েছিল (যা বাস্তবে এটি ছিল) তবে এটি প্রম্পটের পরে আবার উপস্থিত হয়।
প্রম্পটের সামনে উপস্থিত পাঠ্যটি আড়াল করার একটি উপায় হ'ল প্রের্টকে ক্যারিজ রিটার্ন দিয়ে প্রিন্ট করা ( \r
লাইন-ফিড নয়) এবং বর্তমান লাইনটি সাফ করুন <ESC>[M
:
$ PS1="\r\e[M$PS1"
তবে এটি কেবল সেই রেখাটি পরিষ্কার করবে যেখানে প্রম্পট প্রদর্শিত হবে। যদি ইনজেকশন করা পাঠ্যে নতুন লাইনের অন্তর্ভুক্ত থাকে তবে এটি উদ্দেশ্য হিসাবে কাজ করবে না।
অন্য একটি সমাধান ইনজেকশনের অক্ষরগুলির প্রতিধ্বনি অক্ষম করে। একটি মোড়ক এটি করতে ব্যবহার stty
করে:
saved_settings=$(stty -g)
stty -echo -icanon min 1 time 0
inject echo line one
inject echo line two
until read -t0; do
sleep 0.02
done
stty "$saved_settings"
যেখানে inject
উপরে বর্ণিত সমাধানগুলির মধ্যে একটি, বা এর দ্বারা প্রতিস্থাপিত হয়েছে printf '\e[5n'
।
বিকল্প পদ্ধতি
যদি আপনার পরিবেশ নির্দিষ্ট শর্তগুলি পূরণ করে তবে আপনার কাছে অন্যান্য পদ্ধতি উপলব্ধ থাকতে পারে যা আপনি ইনপুট ইনজেক্ট করতে ব্যবহার করতে পারেন। আপনি একটি ডেস্কটপ পরিবেশে থাকেন তাহলে xdotool একটি হল X.Org উপযোগ যে মাউস এবং কীবোর্ডের কার্যকলাপ simulates কিন্তু আপনার ডিস্ট্রো এটি ডিফল্ট নাও থাকতে পারে। আপনি চেষ্টা করতে পারেন:
$ xdotool type ls
আপনি যদি টার্মিনাল মাল্টিপ্লেক্সার tmux ব্যবহার করেন তবে আপনি এটি করতে পারেন:
$ tmux send-key -t session:pane ls
যেখানে -t
নির্বাচন যা অধিবেশন এবং পেন উদ্বুদ্ধ করতে। জিএনইউ স্ক্রিন এর stuff
কমান্ডের সাথে একই রকম ক্ষমতা রয়েছে :
$ screen -S session -p pane -X stuff ls
যদি আপনার ডিস্ট্রোতে কনসোল-সরঞ্জাম প্যাকেজ অন্তর্ভুক্ত থাকে তবে আপনার কাছে একটি writevt
আদেশ থাকতে পারে ioctl
যা আমাদের উদাহরণগুলির মতো ব্যবহার করে । তবে বেশিরভাগ ডিস্ট্রো কেবিডি-র পক্ষে এই প্যাকেজটিকে অবমূল্যায়ন করেছে যা এই বৈশিষ্ট্যটির অভাব রয়েছে।
Writvt.c এর একটি আপডেট কপি ব্যবহার করে সংকলন করা যায় gcc -o writevt writevt.c
।
অন্যান্য ব্যবহারের ক্ষেত্রে কিছু ব্যবহারের ক্ষেত্রে উপযুক্ত হতে পারে এমন প্রত্যাশা এবং খালি অন্তর্ভুক্ত রয়েছে যা ইন্টারেক্টিভ সরঞ্জামগুলিকে স্ক্রিপ্ট করার অনুমতি দেওয়ার জন্য ডিজাইন করা হয়েছে।
আপনি এমন শেলও ব্যবহার zsh
করতে পারেন যা টার্মিনাল ইনজেকশন সমর্থন করে যেমন করতে পারে print -z ls
।
"বাহ, সে চতুর ..." উত্তর
এখানে বর্ণিত পদ্ধতিটি এখানেও আলোচনা করা হয় এবং এখানে আলোচিত পদ্ধতিটির উপর ভিত্তি করে ।
একটি শেল পুনর্নির্দেশ করে /dev/ptmx
একটি নতুন সিউডো-টার্মিনাল পায়:
$ $ ls /dev/pts; ls /dev/pts </dev/ptmx
0 1 2 ptmx
0 1 2 3 ptmx
সি তে লেখা একটি ছোট্ট সরঞ্জাম যা সিউডোটারমিনাল মাস্টার (পিটিএম) কে আনলক করে এবং সিউডোটার্মিনাল ক্রীতদাস (পিটিএস) এর নামটিকে তার স্ট্যান্ডার্ড আউটপুটে আউটপুট করে।
#include <stdio.h>
int main(int argc, char *argv[]) {
if(unlockpt(0)) return 2;
char *ptsname(int fd);
printf("%s\n",ptsname(0));
return argc - 1;
}
(হিসাবে সংরক্ষণ করুন pts.c
এবং এর সাথে সংকলন gcc -o pts pts.c
)
প্রোগ্রামটি যখন কোনও ptm এ স্ট্যান্ডার্ড ইনপুট সেট করে ডাকা হয় তখন এটি সংশ্লিষ্ট পিটিগুলি আনলক করে এবং স্ট্যান্ডার্ড আউটপুটে এর নাম আউটপুট করে।
$ ./pts </dev/ptmx
/dev/pts/20
Unlockpt () ফাংশন ক্রীতদাস pseudoterminal ডিভাইস মাস্টার pseudoterminal দেওয়া ফাইল বর্ণনাকারী দ্বারা উল্লেখ করা সংশ্লিষ্ট আনলক হয়ে যাবে। প্রোগ্রামটি শূন্য হিসাবে পাস করে যা প্রোগ্রামটির মানক ইনপুট ।
Ptsname () ফাংশন আয় ক্রীতদাস pseudoterminal ডিভাইস মাস্টার আনুসঙ্গিক নাম দেওয়া ফাইল বর্ণনাকারী দ্বারা উল্লেখ করা হয়েছে, আবার প্রোগ্রামের মান ইনপুট জন্য শূন্য ক্ষণস্থায়ী।
একটি প্রক্রিয়া pts সংযুক্ত করা যেতে পারে। প্রথমে একটি পিটিএম পান (এখানে এটি ফাইল বর্ণনাকারী 3 অর্পণ করা হয়েছে, <>
পুনর্নির্দেশের দ্বারা পঠন-লিখন খোলা হয়েছে )।
exec 3<>/dev/ptmx
তারপরে প্রক্রিয়াটি শুরু করুন:
$ (setsid -c bash -i 2>&1 | tee log) <>"$(./pts <&3)" 3>&- >&0 &
এই কমান্ড-লাইন দ্বারা উদ্ভূত প্রক্রিয়াগুলি এর সাথে সর্বোত্তম চিত্রিত pstree
:
$ pstree -pg -H $(jobs -p %+) $$
bash(5203,5203)─┬─bash(6524,6524)─┬─bash(6527,6527)
│ └─tee(6528,6524)
└─pstree(6815,6815)
আউটপুট বর্তমান শেল ( $$
) এর সাথে সম্পর্কিত এবং প্রতিটি প্রক্রিয়াটির পিআইডি ( -p
) এবং পিজিআইডি ( -g
) বন্ধনীতে দেখানো হয়েছে (PID,PGID)
।
গাছের bash(5203,5203)
শিরোনামে, ইন্টারেক্টিভ শেল যেটিতে আমরা কমান্ডগুলি টাইপ করছি এবং এর ফাইল বর্ণনাকারীরা এটি টার্মিনাল অ্যাপ্লিকেশনের সাথে সংযুক্ত করে আমরা এটির ( xterm
বা অনুরূপ) সাথে ইন্টারেক্ট করার জন্য ব্যবহার করছি ।
$ ls -l /dev/fd/
lrwx------ 0 -> /dev/pts/3
lrwx------ 1 -> /dev/pts/3
lrwx------ 2 -> /dev/pts/3
কমান্ডটি আবার দেখলে, প্রথম বন্ধনীগুলির প্রথম সেটটি সাবসেল শুরু করেছিল, bash(6524,6524)
যার ফাইল ডেস্ক্রিপ্টার 0 (এর স্ট্যান্ডার্ড ইনপুট ) পিটিএসকে দেওয়া হয়েছিল (যা রিডিং-রাইট খোলা হয় <>
) অন্য আনুষাঙ্গিকে ./pts <&3
আনলক করার জন্য কার্যকর করা হয়েছিল পিটিএস ফাইল বর্ণনাকারী 3 (পূর্ববর্তী ধাপে তৈরি, exec 3<>/dev/ptmx
) এর সাথে সম্পর্কিত।
সাবশেলের ফাইল ডেস্ক্রিপ্টর 3 টি বন্ধ ( 3>&-
) যাতে পিটিএম এর কাছে অ্যাক্সেসযোগ্য না থাকে। এর স্ট্যান্ডার্ড ইনপুট (এফডি 0), যা পড়তে / লেখার জন্য খোলা pts, এটি >&0
তার স্ট্যান্ডার্ড আউটপুট (এফডি 1) এ পুনঃনির্দেশিত হয় (আসলে fd অনুলিপি করা হয় )।
এটি পিটিএসের সাথে সংযুক্ত তার স্ট্যান্ডার্ড ইনপুট এবং আউটপুট সহ একটি সাবশেল তৈরি করে। এটি পিটিএম লিখে লিখে ইনপুট প্রেরণ করা যায় এবং পিটিএম থেকে পড়ে এর আউটপুটটি দেখা যায়:
$ echo 'some input' >&3 # write to subshell
$ cat <&3 # read from subshell
সাবশেল এই আদেশটি কার্যকর করে:
setsid -c bash -i 2>&1 | tee log
এটি একটি নতুন সেশনে bash(6527,6527)
ইন্টারেক্টিভ ( -i
) মোডে setsid -c
চলবে ( , নোট করুন পিআইডি এবং পিজিআইডি একই রকম)। এটির স্ট্যান্ডার্ড ত্রুটিটি তার স্ট্যান্ডার্ড আউটপুট ( 2>&1
) এ পুনঃনির্দেশিত হয় এবং পাইপ করা হয় tee(6528,6524)
যাতে এটি কোনও log
ফাইলে এবং পিটিএসে লেখা থাকে। এটি সাবসেলের আউটপুটটি দেখার আরও একটি উপায় দেয়:
$ tail -f log
সাবসেল bash
ইন্টারেক্টিভভাবে চলমান হওয়ায় , এটি কার্যকর করার জন্য কমান্ড প্রেরণ করা যেতে পারে, উদাহরণস্বরূপ যা সাবশেলের ফাইল বর্ণনাকারীদের প্রদর্শন করে:
$ echo 'ls -l /dev/fd/' >&3
সাবশেলের আউটপুট ( tail -f log
বা cat <&3
) পড়ার বিষয়টি প্রকাশ করে:
lrwx------ 0 -> /dev/pts/17
l-wx------ 1 -> pipe:[116261]
l-wx------ 2 -> pipe:[116261]
স্ট্যান্ডার্ড ইনপুট (এফডি 0) পিটিএসের সাথে সংযুক্ত এবং স্ট্যান্ডার্ড আউটপুট (এফডি 1) এবং ত্রুটি (এফডি 2) উভয়ই একই পাইপের সাথে সংযুক্ত রয়েছে, এটি যা সংযোগ করে tee
:
$ (find /proc -type l | xargs ls -l | fgrep 'pipe:[116261]') 2>/dev/null
l-wx------ /proc/6527/fd/1 -> pipe:[116261]
l-wx------ /proc/6527/fd/2 -> pipe:[116261]
lr-x------ /proc/6528/fd/0 -> pipe:[116261]
এবং ফাইল বর্ণনাকারী এক নজরে tee
$ ls -l /proc/6528/fd/
lr-x------ 0 -> pipe:[116261]
lrwx------ 1 -> /dev/pts/17
lrwx------ 2 -> /dev/pts/3
l-wx------ 3 -> /home/myuser/work/log
স্ট্যান্ডার্ড আউটপুট (এফডি 1) হ'ল পিটিএস: 'টি' তার স্ট্যান্ডার্ড আউটপুটটিতে যে কোনও কিছুই লিখে দেয় তা পিটিএম এ ফিরে পাঠানো হয়। স্ট্যান্ডার্ড ত্রুটি (এফডি 2) হ'ল নিয়ন্ত্রণকারী টার্মিনালের অন্তর্গত pts।
এটিকে গুটিয়ে রাখা
নীচের স্ক্রিপ্ট উপরে বর্ণিত কৌশল ব্যবহার করে। এটি একটি ইন্টারেক্টিভ bash
সেশন সেট আপ করে যা কোনও ফাইল বর্ণনাকারীর কাছে লিখে সংক্রামিত হতে পারে। এটি এখানে উপলব্ধ এবং ব্যাখ্যা সহ নথিভুক্ত।
sh -cm 'cat <&9 &cat >&9|( ### copy to/from host/slave
trap " stty $(stty -g ### save/restore stty settings on exit
stty -echo raw) ### host: no echo and raw-mode
kill -1 0" EXIT ### send a -HUP to host pgrp on EXIT
<>"$($pts <&9)" >&0 2>&1\
setsid -wc -- bash) <&1 ### point bash <0,1,2> at slave and setsid bash
' -- 9<>/dev/ptmx 2>/dev/null ### open pty master on <>9